8

对React Hooks的Capture value特性的理解

 3 years ago
source link: https://www.daozhao.com/10075.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

对React Hooks的Capture value特性的理解

如果您发现本文排版有问题,可以先点击下面的链接切换至老版进行查看!!!

对React Hooks的Capture value特性的理解

之前我的项目里面很多功能都是用的事件驱动,所以下面的实例也会更多地使用监听事件的回调函数。

我们先看下测试代码

const {useEffect ,useState, useRef, useMemo} = React;
const {render} = ReactDOM;
const eventBus = new EventEmitter();

function ListenButton() {
  const [started, setStarted] = useState(false);
  const [score, setScore] = useState(0);

  function onStartedClick() {
    setStarted(!started);
  }

  function onClick() {
    setScore(score + 1);
  }

  function onBouns() {
    setScore(score + 2);
  }

  const isWin = useMemo(() => {
    return started && score > 4
  }, [started, score])

  useEffect(() => {
    eventBus.on('bonus', onBouns);
  }, [])

  return (
    <div>
      <div>
        <p>started: { started ? 'Yes' : 'No'}</p>
        <p>score: { score }</p>
        <p>WIN: { isWin ? 'Yes' : 'No' }</p>
      </div>
      <button onClick={onStartedClick}>{started ? 'STOP' : 'START'}</button>
      <button onClick={onClick}>AddScore</button>
   </div>
  )
}

function EmitButton() {
  function emit() {
    eventBus.emit('bonus');
  }

  return (
    <button onClick={emit}>bonus</button>
  )
}

function App () {
  return (
    <div>
      <EmitButton />
      <ListenButton />
    </div>
  )
}
render( <App />, document.querySelector('#app'))

file

当我们点击bonus按钮时直接在当前score的基础上加2分,首次点击时score从0变成2,再次点击呢? 还是2,因为在当时监听bonus事件时score的值为初始值0,而不是我们期望的最新值2

这也就是我们常说的Capture Value特性,也有叫闭包陷阱的说法。

这是官方特意设置的机制,官方原文是:

This prevents bugs caused by the code assuming props and state don’t change;

(我们可以这么理解:防止因 React 认为 props 或者 state 没有变更而引起的 bug)

怎样才能获得最新的score的值呢??? 方案一 加入依赖

  useEffect(() => {
    eventBus.on('bonus', onBouns);
  }, [score])

加入依赖性score,那样我们的onBouns方法就每次score变更时更新onBouns了。

方案二 用useRef

const scoreRef = useRef();
scoreRef.current = score;

function onBouns() {
  setScore(scoreRef.current + 2);
}

useRef可以用来存储任何可变数据,我们可以在每次score变更的同时重新赋值scoreRef.current,这样我们就借用useRef绕过了Capture Value特性。

我们可以这样理解: 每次 Render 的内容都会形成一个快照并保留下来,因此当状态变更而 Rerender 时,就形成了 N 个 Render 状态,而每个 Render 状态都拥有自己固定不变的 Props 与 State。

有些时候(像上面的onBonus)可以比较简单在useEffect里面加入依赖,有的时候就比较麻烦了,我们只能依赖useRef暂时绕过这个问题了。

PS: useMemo也有依赖,它也有类似的问题,有时间改造下这个例子再看一下。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK