const { useState, useCallback } = React;
let globalValue = 0;
const ComponentA = ({ propValue }) => {
const [sum, setSum] = useState(propValue);
const [stateValue, setStateValue] = useState(0);
const onClick = useCallback(() => {
console.log(propValue, stateValue, globalValue);
setSum(propValue + stateValue + globalValue);
}, []);
return (
<div>
<p>State value is {stateValue}</p>
<p>Sum is {sum}</p>
<button onClick={() => setStateValue(stateValue + 1)}>
Set stateValue
</button>
<button onClick={() => globalValue += 1}>
Set globalValue
</button>
<button onClick={onClick}>
Get Sum
</button>
</div>
);
};
const Parent = () => {
const [propValue, setPropValue] = useState(0);
return (
<div>
<p>Prop value is {propValue}</p>
<button onClick={() => setPropValue(propValue + 1)}>
Set PropValue
</button>
<ComponentA propValue={propValue} />
</div>
);
};
如果ComponentA
中useCallback
的依赖列表设置为[]
,则在执行console.log
时,无论当前propValue
和stateValue
是什么,它们都将始终0
。但globalValue
将始终使用最新版本。当我检查propValue
和stateValue
的__proto__
时,它们都只是原始类型。
useCallback
钩子如何保持变量的原始值?它不应该尊重这样的规则,即如果一个变量没有在当前范围内定义,它将查找它的父变量并使用那里的值(如globalValue
(?
我知道这是useCallback/useMemo
的重点,但这是如何实现的?
我从这个博客中找到了答案 https://dmitripavlutin.com/react-hooks-stale-closures/
下面是 useCallback 的模拟代码。
(function () {
const ReactDomTree = {};
const memo = {};
const getButtonElement = () => { return ReactDomTree.buttonA };
function useMemo(create) {
if (!memo.result) {
memo.result = create();
}
return memo.result;
};
const useCallback = function (callback) {
return useMemo(() => callback);
}
function component() {
let propValue = Math.floor(Math.random() * 100); // mimic prop update
let stateValue = Math.floor(Math.random() * 100); // mimic state update
console.log('component render', propValue, stateValue);
const onClick = useCallback(() => {
console.log('onclick', propValue, stateValue);
});
if (!ReactDomTree.buttonA) {
ReactDomTree.buttonA = {};
ReactDomTree.buttonA.onClick = onClick;
}
}
component(); // component render
let button = getButtonElement()
button.onClick();
component(); // component render
button = getButtonElement()
button.onClick() // This log result is same as the previous one
})();