[英]React Hook - I always get stale values from useState just because child component never updates
TL;DR This is my Parent component: TL; DR这是我的父级组件:
const Parent = () => {
const [open, setOpen] = useState([]);
const handleExpand = panelIndex => {
if (open.includes(panelIndex)) {
// remove panelIndex from [...open]
// asign new array to variable: newOpen
// set the state
setOpen(newOpen);
} else {
setOpen([...open, panelIndex]);
}
}
return (
<div>
<Child expand={handleExpand} /> // No need to update
<Other isExpanded={open} /> // needs to update if open changed
</div>
)
}
And this is my Child
component: 这是我的
Child
组件:
const Child = (props) => (
<button
type="button"
onClick={() => props.expand(1)}
>
EXPAND PANEL 1
</button>
);
export default React.memo(Child, () => true); // true means don't re-render
Those code are just an example. 这些代码只是一个例子。 The main point is I don't need to update or re-render
Child
component because it just a button. 要点是,我不需要更新或重新渲染
Child
组件,因为它只是一个按钮。 But the second time I click the button it's not triggering Parent
to re-render. 但是第二次单击该按钮并没有触发
Parent
重新渲染。
If I put console.log(open)
inside handleExpand
like so: 如果我将
console.log(open)
放入handleExpand
如下所示:
const handleExpand = panelIndex => {
console.log(open);
if (open.includes(panelIndex)) {
// remove panelIndex from [...open]
// asign new array to variable: newOpen
// set the state
setOpen(newOpen);
} else {
setOpen([...open, panelIndex]);
}
}
it printed out the same array everytime I clicked the button as if the value of open
which is array never updated. 每当我单击按钮时,它就会打印出相同的数组,好像
open
的值从未更新过。
But if I let <Child />
component re-render when open
changed, it works. 但是,如果在
open
更改时让<Child />
组件重新渲染,则它可以工作。 Why is that? 这是为什么? is this something as expected?
这是预期的吗?
This is indeed expected behavior. 这确实是预期的行为。
What you are experiencing here are function closures. 您在这里遇到的是函数闭包。 When you pass
handleExpand
to Child all referenced variables are 'saved' with their current value. 当将
handleExpand
传递给Child时,所有引用的变量及其当前值都被“保存”。 open = []
. open = []
。 Since your component does not re-render it will not receive a 'new version' of your handleExpand
callback. 由于您的组件不会重新渲染,因此不会收到
handleExpand
回调的“新版本”。 Every call will have the same result. 每次通话都会有相同的结果。
There are several ways of bypassing this. 有几种绕过此方法的方法。 First obviously being letting your Child component re-render.
首先显然是让您的Child组件重新渲染。
However if you strictly do not want to rerender you could use useRef
which creates an object and access it's current property: 但是,如果您严格不希望重新渲染,则可以使用
useRef
创建一个对象并访问它的当前属性:
const openRef = useRef([])
const [open, setOpen] = useState(openRef.current);
// We keep our ref value synced with our state value
useEffect(() => {
openRef.current = open;
}, [open])
const handleExpand = panelIndex => {
if (openRef.current.includes(panelIndex)) {
setOpen(newOpen);
} else {
// Notice we use the callback version to get the current state
// and not a referenced state from the closure
setOpen(open => [...open, panelIndex]);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.