对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'))
现状
当我们点击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
也有依赖,它也有类似的问题,有时间改造下这个例子再看一下。
- 分类:
- Web前端
相关文章
Did you mean to use React.forwardRef()?搞懂react的createRef和forwardRef
最近在使用react过程中发现在使用ref时的一些场景,自己初步感觉react的ref没有vue那么强大。 现在我就简单看下怎么使用ref? createRef 我们直接看源码 / 阅读更多…
React router用hooks读取routeName、根据routeName跳转
在迁移Vue至React的过程中遇到了一些路由相关的问题,在Vue项目中经常会使用routeName,毕竟使用path太长了,也记不住,我自己看了看React router也没有发现routeNam 阅读更多…
vue迁移react使用useReducer hooks还想支持回调?
鉴于目前有个任务是把手上的vue项目迁移到react,为了尽可能的降低期间造成的功能缺陷,很自然的就会想到使用一个方法一个方法的迁移,有的场景在vue中很自然,在react中,尤其是在使用react 阅读更多…
怎么调试Webpack+React项目,报错basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")怎么办
今天在WebStorm上Windows上准备调试一个React项目,就出现了这样的报错。 Node Parameters里面写的是webpack-dev-server的执行文件 .\node_mod 阅读更多…
转译:使用react hooks优化回调函数在组件间的传递,useState,useReducer?
我们先看一下使用 useState hooks写的todoList组件,里面我们需要层层传递回调函数。 import React, { useState } from "react& 阅读更多…
React执行调度流程梳理笔记
触发更新 我们最常见的触发更新的方式就是更新state了,可以分别从类组件和函数组件看看这时会发生什么。 类组件之 setState: 当触发 setState 本质上是调用 enque 阅读更多…