Re: 新手让网页 act 起来: Day17 - 探索 useEffect

昨天介实作一个阳春的 useState ,今天来换 useEffect 吧!透过实作来帮助我们以後在写这两个 hooks 的时候,能够更有画面!

myUseEffect

先来看看我们前两天完成的 useState 的程序码

const hooks = [] // 使用阵列来收集每一次 hook 呼叫
let callId = 0 // 用来纪录每一次 hook 呼叫的顺序

function myUseState(initialValue) {
  const id = callId++
  if (hooks[id]) return hooks[id]

  function setState(newValue) {
    let nextValue
    typeof newValue === 'function' ? nextValue = newValue(hooks[id][0]) : nextValue = newValue
    if (hooks[id][0] === nextValue) return

    hooks[id][0] = nextValue
    callRender()
  }

  const stateArray = [initialValue, setState]
  hooks[id] = stateArray

  return stateArray
}

const Counter = () => {
 //.....
}

function callRender() {
  callId = 0
  ReactDOM.render(<Counter />, document.getElementById('root'))
}
callRender()

我们在最外面设置了 hooks 阵列与 callId,来帮助我们纪录每一个 hook 被呼叫的顺序以及该纪录的值。所以我们就可以利用这点,来纪录 useEffect 中的 dependency array 。 为什麽要纪录呢?就让我们先来快速复习一下 useEffect 吧!

  1. 如果没有给二个参数,在每一次 render 後都会执行第一个 callback 参数
  2. 会根据第二个阵列参数中的值,来决定是否执行第一个 callback 参数

所以要完成第二点,我们就需要利用 hooks array 来纪录 dependency array,比对每一次 re-render 传进来的 dependencies 与纪录在 hooks 中的是不是都一样。

接下来我们就来尝试完成 myUseEffect 吧!

  1. 当第一次呼叫时,执行 callback ,并将 dependency 存入 hooks 并增加 callId
function myUseEffect(callback, depArray) {
  callback()
  hooks[callId] = depArray
  callId++
}
  1. 之後每一次呼叫要取得之前纪录的 dependency
function myUseEffect(callback, depArray) {
  const prevDepArray = hooksp[callId]
  
  callback()
  hooks[callId] = depArray
  callId++
}
  1. 比对先前纪录的与当下的 dependency ,如果有变化就执行 callback
function myUseEffect(callback, depArray) {
  const prevDepArray = hooksp[callId]
  let hasChange = true
  
  if (prevDepArray) {
    hasChange = depArray.some(
      (dep, idx) => !Object.is(dep, prevDepArray[idx])
    )
  }
  
  if (hasChange) callback()
  
  hooks[callId] = depArray
  callId++
}
  1. 每一次 render 後才执行 callback
function myUseEffect(callback, depArray) {
  const prevDepArray = hooksp[callId]
  let hasChange = true
  
  if (prevDepArray) {
    hasChange = depArray.some(
      (dep, idx) => !Object.is(dep, prevDepArray[idx])
    )
  }
  
  if (hasChange) {
    setTimeout(() => {
      callback()
    }, 100)
  }
  
  hooks[callId] = depArray
  callId++
}

这样子,我们就土炮完成了 myUseEffect 了!

接下来,来试试看它是不是能够运作,将 Counter 元件,加上 myUseEffect ,当 count 变化时,改变 document.title 。

const Counter = () => {
  const [count, setCount] = myUseState(0)
  const [count2, setCount2] = myUseState(10)

  myUseEffect(() => {
    console.log('只执行第一次')
  }, [])

  myUseEffect(() => {
    document.title = `Click ${count} times`
  }, [count])

  myUseEffect(() => {
    console.log('只执行第一次,除非改变 count2')
  }, [count2])

  const increaseHandler = () => {
    setCount(prev => prev + 1)
  }

  const decreaseHandler = () => {
    if (count === 0) return
    setCount(prev => prev - 1)
  }

  return (
    <div className="container">
      <button className="minus" onClick={decreaseHandler}>-</button>
      <span className="number">{count}</span>
      <button className="plus" onClick={increaseHandler}>+</button>
    </div>
  )
};

顺利的话,就会看到当我们按下 count ,document.title 会如预期改变,另外两个 myUseEffect 都只会第一次执行。

以上就是今天的介绍,如果有任何问题都欢迎在下方留言,明天我们将在继续介绍其他的 hooks!

该文章同步发布於:我的部落格


<<:  Day17 - [丰收款] 永丰API虚拟帐户付款与PayToken查询与更新状态

>>:  Day 17: Structural patterns - Proxy

Vue 如何在 LocalHost 开发环境时使用 HTTPS

如果你有 Localhost 开发环境需要以 HTTPS 浏览时,可以参考以下方法: 方法一:vue...

Day13 iPhone捷径-媒体Part3

Hello 大家, 今天第一个要讲的是拍摄萤幕快照, 嘿嘿~就是截图, 没啥好说的XD, 但截图後面...

Day13 - PDF 加密、解密的处理

前言 在处理 PDF 增加密码 (加密) 、移除解密 (解密) 时,可以使用 Ghostscript...

【Docker】03 使用Wordpress官方Image

摘要 建立一个 container 集合体,包含两个 container, db 跟 wordpre...

【Day 1】大纲 / 30 天的学习计画

身为一个普大的普通大学生, 实战经验少得可怜, 除了学过资工基本学科,就只会用 Python 写 L...