简体   繁体   English

使用 React.memo 和 useCallback 时 State 未正确更新

[英]State is not updated properly when using React.memo and useCallback

My problem is quite common but it is not easy to figure out what is wrong with my example.我的问题很常见,但要弄清楚我的示例有什么问题并不容易。 I have nested components Parent->Child1->Child2.我有嵌套组件 Parent->Child1->Child2。 State and setState are passed in props to child components. State 和 setState 在 props 中传递给子组件。 I want to avoid re-renders in child components when parent is updated and I want to avoid to re-render the whole list when only one item is changed.我想避免在更新父组件时重新渲染子组件,并且我想避免在仅更改一项时重新渲染整个列表。

Here is my sample https://plnkr.co/edit/6nKaKrgNIF7LSetN As you can see when I change title in Parent or description in Child1, Child2 is not re-rendered, but when I change state in Child2, whole list is re-rendered plus there are some side-effects with state update.这是我的示例https://plnkr.co/edit/6nKaKrgNIF7LSetN正如您所看到的,当我更改 Parent 中的标题或 Child1 中的描述时,Child2 不会重新呈现,但是当我在 Child2 中更改 state 时,整个列表是重新渲染加上 state 更新有一些副作用。

Parent component.父组件。 It contains title and.它包含标题和。 For Child1 onChange I receive prop which I want to update in state and value for it.对于 Child1 onChange,我收到了我想在 state 中更新的道具和它的价值 I use useCallback hook to avoid additional re-render in child components, you will see React.memo there我使用 useCallback 钩子来避免子组件中的额外重新渲染,你会在那里看到 React.memo


const Parent = ({ initialData }) => {
    const [data, setData] = React.useState(initialData);
    console.log('render Parent');
    return <>
         <h2>Parent</h2>

        <input
            placeholder="title"
            value={data.title}
            onChange={
                React.useCallback((e) => {
                     setData(prev => ({ ...prev, title: e.target.value }))
                }, [])
            }
        />
        <br />
        <Child1
            data={data}
            onChange={
                React.useCallback((prop, value) => {
                    setData(
                        prev => {
                            prev[prop] = value;
                            const newState = { ...prev };
                            return newState;
                        }
                    );
                }
                , []
                )
            }

        />
    </>
}


Then in Child1 wrapped with React.memo.然后在用 React.memo 包裹的 Child1 中。 It renders also Child2 in a list.它还在列表中呈现 Child2。

        const Child1 = React.memo(
            ({ data, onChange }) => {
                console.log('render Child1');
                return <>
                    <h3>Child1</h3>

                    <input
                        placeholder="description"
                        value={data.description}
                        onChange={(e) => { onChange('description', e.target.value) }}

                    />
                    <br />
                    <br />
                    <br />
                    {data.list.map((element, index) => {
                        return <Child2
                            key={index} // don't do this in real
                            index={index}
                            data={element}
                            onChange={
                                React.useCallback(
                                    (prop, value) => {
                                        const newList = data.list.map((e, i) => {
                                            let newItem = { ...e };
                                            if (i == index) {
                                                newItem[prop] = value;
                                            }
                                            return newItem;
                                        });
                                        onChange('list', newList);
                                    }
                                    ,
                                    []
                                )
                            }
                        />
                    })}

                </>

            }
        )

Child2 is rendered in the list Child2 在列表中呈现


        const Child2 = React.memo(({ index, data, onChange }) => {
            console.log('render Child2', index);
            return (

                <>
                    <h4>Child2</h4>

                    Country: <br />
                    <input
                        placeholder="country"
                        value={data.country}
                        onChange={(e) => onChange('country', e.target.value)}
                    />
                    <br />
                    <br />
                    Region: <br />
                    <input
                        placeholder="region"
                        value={data.region}
                        onChange={(e) => onChange('region', e.target.value)}
                    />
                    <br />
                    <br />
                    City: <br />
                    <input
                        placeholder="city"
                        value={data.city}
                        onChange={(e) => onChange('city', e.target.value)}
                    />

                    <hr />

                </>

            )
        }

        )



onChange in Child2 is calling setData from Parent, which updates Parent's state. Child2 中的onChange正在从 Parent 调用setData ,这会更新 Parent 的 state。 Updating the state of Parent will trigger a re-render.更新 Parent 的 state 将触发重新渲染。 Child1 re-renders because its data prop is changing (changing props will also trigger a re-render). Child1 重新渲染是因为它的data prop 正在改变(改变 props 也会触发重新渲染)。

Try passing title in your dependency arrays in the useCallback functions in Parent and Child1 and see if that works.尝试在 Parent 和 Child1 的 useCallback 函数中传递您的依赖项 arrays 中的title ,看看是否有效。 This should make the onChange function appear to be "changing" which should trigger re-renders down the component tree.这应该使 onChange function 看起来正在“改变”,这应该会触发组件树的重新渲染。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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