简体   繁体   English

React Hook-我总是从useState获得陈旧的值,只是因为子组件永远不会更新

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM