你好同学,useMemo/useCallback/useEffect第二个参数是一致的,都可以传入任意复杂度的同步计算表达式。
根据你的描述,我推测出下面的代码:
function App() {
const [clickCount, setClickCount] = useState(0);
const handleClick = useCallback(() => {
setClickCount(clickCount + 1);
}, [clickCount === 2]);
return (
<div>
<button onClick={handleClick}>Press</button>
<span>{clickCount}</span>
</div>
)
}
export default App;
多次点击按钮,显示的clickCount一直是1,这是正确的。
这是因为,clickCount === 2这个表达式的值没变,造成了handleClick没变,同时里面引用的上下文也没变,clickCount一直停留在0的状态,即使加1,最多也就变成1,并不会再变了。
具体是这样的过程:
首次渲染,handleClick所指向的这个函数,它读取到的上下文是 {clickCount: 0};
点击按钮,调用 setClickCount,clickCount变成1,触发第二次渲染;
第二次渲染,计算useCallback的第二个参数,发现依然是false,那么useCallback依然会返回之前的函数给 handleClick,handleClick内部能读取到的 clickCount 依然是0;
再次点击按钮,调用handleClick,setClickCount还是只能传入 0+1,还是1,React发现数据相同,不会触发重新渲染;
一直保持这个状态
之所以出现这个问题是你实际上引入了一个闭包,也就是handleClick引用了外部上下文的数据,而作为函数组件,每次重新渲染,都会创建一个新的上下文,而handleClick句柄指向的还是旧上下文。
解决这个问题很简单,就是传入函数给 setClickCount:
setClickCount(count => count + 1)
这样并没有引用任何上下文的数据,因此是安全的。当然把useCallback的第二个参数改成 [clickCount] 或者不加第二个参数也能解决问题。
作为第二个依赖参数,没有满足与不满足的说法,只有变化与未变化,只有变化了才会重新执行,第一次渲染的时候,无从对比,都会先执行一次,不论useMemo/useCallback还是useEffect。
祝您学习愉快!