React hooks:React.memo 和 useState 的问题 [英] React hooks: Issue with React.memo and useState
问题描述
我尝试了 React 钩子,但遇到了一个我不明白的问题.
代码在这里https://codesandbox.io/embed/hnrch-hooks-issue-dfk0t该示例有 2 个简单的组件:
应用组件
const App = () =>{const [num, setNum] = useState(0);const [str, setStr] = useState();const inc = () =>{setNum(num + 1);setStr(num >= 5 ? ">= 5" : "< 5");控制台日志(数量);};const button = <button onClick={inc}>++</button>;console.log("父重新渲染", num);返回 (<div className="应用程序"><h1>应用</h1><Child str={str}>{button}</Child>
);};
子组件
const Child = React.memo(({ str, children }) =>{console.log("子重新渲染");返回 (<div><><h2>功能儿童</h2><p>{str}</p>{孩子们}</>
);}(上一个,道具)=>{return prev.str === props.str;});
所以我将孩子包裹在 React.memo
中,并且只有在 str
不同时才应该重新渲染.但是它也有子级,并且它传递了一个按钮,该按钮在父级(应用程序)内增加一个计数器.
问题是:计数器在设置为 1 后将停止递增.有人可以向我解释这个问题以及我需要了解什么才能避免这些错误吗?
这是一个关闭问题".
这是App
的第一次渲染,第一次创建了inc
函数:(我们称之为inc#1
)
const inc = () =>{setNum(num + 1);setStr(num >= 5 ? ">= 5" : "< 5");控制台日志(数量);};
在 inc#1
范围内,num
当前为 0
.然后将该函数传递给 button
,然后将其传递给 Child
.
到目前为止一切都很好.现在你按下按钮,inc#1
被调用,这意味着
setNum(num + 1);
num === 0
发生的地方.App
被重新渲染,但是 Child
不是.条件是如果 prev.str === props.str
然后我们不再渲染 Child.
我们现在处于 App
的第二次渲染中,但是 Child
仍然拥有 inc#1
实例,其中 num
是 0
.
你现在知道问题出在哪里了吗?您仍将调用该函数,但 inc
现在已过时.
您有多种方法可以解决此问题.您可以确保 Child
始终具有更新的道具.
或者您可以将回调传递给 setState
以获取当前值(而不是位于闭包范围内的陈旧值).这也是一种选择:
const inc = () =>{setNum((currentNum) => currentNum + 1);};React.useEffect(() => {setStr(num >= 5 ? ">= 5" : "< 5");}, [编号])
I played around with react hooks, and I came across an issue that I do not understand.
The code is here https://codesandbox.io/embed/hnrch-hooks-issue-dfk0t The example has 2 simple components:
App Component
const App = () => {
const [num, setNum] = useState(0);
const [str, setStr] = useState();
const inc = () => {
setNum(num + 1);
setStr(num >= 5 ? ">= 5" : "< 5");
console.log(num);
};
const button = <button onClick={inc}>++</button>;
console.log("parent rerender", num);
return (
<div className="App">
<h1>App</h1>
<Child str={str}>{button}</Child>
</div>
);
};
Child Component
const Child = React.memo(
({ str, children }) => {
console.log("child rerender");
return (
<div>
<>
<h2>Functional Child</h2>
<p>{str}</p>
{children}
</>
</div>
);
}
(prev, props) => {
return prev.str === props.str;
}
);
So I have the child wrapped in a React.memo
, and it should only rerender if str
is different. But is also has children, and it get's passed a button which is incrementing a counter inside the parent (App).
The issue is: The counter will stop incrementing after it is set to 1. Can someone explain this issue to me and what I need to understand to avoid those bugs?
It's a "closure issue".
This is the first render time for App
, the inc
function has been created the first time: (let's call it inc#1
)
const inc = () => {
setNum(num + 1);
setStr(num >= 5 ? ">= 5" : "< 5");
console.log(num);
};
In the inc#1
scope, num
is currently 0
. The function is then passed to button
which is then passed to Child
.
All good so far. Now you press the button, inc#1
is invoked, which mean that
setNum(num + 1);
where num === 0
happen. App
is re-rendered, but Child
is not. The condition is if prev.str === props.str
then we don't render again Child.
We are in the second render of App
now, but Child
still own the inc#1
instance, where num
is 0
.
You see where the problem is now? You will still invoke that function, but inc
is now stale.
You have multiple ways to solve the issue. You could make sure that Child
has always the updated props.
Or you could pass a callback to setState
to fetch the current value ( instead of the stale one that live in the scope of the closure ). This is also an option:
const inc = () => {
setNum((currentNum) => currentNum + 1);
};
React.useEffect(() => {
setStr(num >= 5 ? ">= 5" : "< 5");
}, [num])
这篇关于React hooks:React.memo 和 useState 的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!