繁体   English   中英

React.useMemo 正在重新渲染数组中的所有项目

[英]React.useMemo is re-rendering all items in array

我有一个反应组件,它在 useState 中存储一组水果。 我有一个过滤水果的记忆 function (visibleFruits)。 我 map 对 dom 可见水果。

问题是,当我检查一个水果时,所有可见的水果都会重新渲染。

我期望只有选定的一个重新渲染,因为它是唯一一个正在改变的。

有没有办法让我使用这种模式,但防止所有人在检查时重新渲染?

在现实生活中,可见水果中的使用备忘录中有一个复杂的 function。 所以我不能简单地 append 过滤器在 map 之前。

编辑,这里是更新的编辑:


const Example = () => {
    const [fruits, setFruits] = React.useState([
        { id: 1, name: 'apple', visible: true, selected: false },
        { id: 2, name: 'banana', visible: false, selected: false },
        { id: 3, name: 'orange', visible: true, selected: false }
    ])

    const visibleFruits = React.useMemo(() => {
        return fruits.filter((f) => f.visible)
    }, [fruits])

    const handleCheck = (bool, id) => {
        setFruits((prev) => {
            return prev.map((f) => {
                if (f.id === id) {
                    f.selected = bool
                }
                return f
            })
        })
    }

    return (
        <div>
            {visibleFruits.map((fruit) => {
                return <FruitOption fruit={fruit} handleCheck={handleCheck} />
            })}
        </div>
    )
}

const FruitOption = ({ fruit, handleCheck }) => {
    console.log('** THIS RENDERS TWICE EVERY TIME USER SELECTS A FRUIT **')
    return (
        <div key={fruit.id}>
            <input
                checked={fruit.selected}
                onChange={(e) => handleCheck(e.target.checked, fruit.id)}
                type='checkbox'
            />
            <label>{fruit.name}</label>
        </div>
    )
}

export default Example

首先, handleCheck function 有问题(但这与您要问的问题无关)。 您的代码正在修改fruit object 直接( f.selected = bool ),但不允许您使用 React state 执行此操作,state中的对象如果您违反该规则,则不能直接修改,并且渲染可能不正确。 相反,您需要复制 object 并修改副本(就像您使用数组一样):

const handleCheck = (bool, id) => {
    setFruits((prev) => {
        return prev.map((f) => {
            if (f.id === id) {
                return {...f, selected: bool}; // ***
            }
            return f;
        });
    });
};

但这不是您要问的,只是要解决的其他问题。 :-)

您看到在handleCheck之后执行了两次console.log的原因是该组件必须重新渲染(用于更改),并且有两个可见的水果,因此您会看到对FruitOption组件 function 的两次调用。 有两个原因:

  1. 每次调用Example组件 function 时, handleChange发生变化,因此FruitOption每次都会看到一个新道具;

  2. FruitOption在其 props 不变时不会避免重新渲染,因此即使您修复了 #1,您仍然会看到两个console.log调用;

另外, FruitOption元素上没有key ,这可能会导致呈现问题。 渲染数组中的元素时始终包含有意义的键。 (不要只使用索引, 这是有问题的;但是你的水果对象有一个id ,这是完美的。)

要解决这个问题:

  1. 记住handleChange以便它不会每次都重新创建,可能是通过useCallback

  2. 使用React.memo以便FruitOption在其 props 不变时不会被调用,并且

  3. Example中的FruitOption元素添加一个有意义的key

将这些和上面的handleChange修复并将它们放在一起:

 const Example = () => { const [fruits, setFruits] = React.useState([ { id: 1, name: 'apple', visible: true, selected: false }, { id: 2, name: 'banana', visible: false, selected: false }, { id: 3, name: 'orange', visible: true, selected: false } ]); const visibleFruits = React.useMemo(() => { return fruits.filter((f) => f.visible); }, [fruits]); const handleCheck = React.useCallback( (bool, id) => { setFruits((prev) => { return prev.map((f) => { if (f.id === id) { return {...f, selected: bool}; // *** } return f; }); }); }, [] // *** No dependencies since all it uses is `setFruits`, which is // stable for the lifetime of the component ); return ( <div> {visibleFruits.map((fruit) => { // *** Note the key return <FruitOption key={fruit.id} fruit={fruit} handleCheck={handleCheck} /> })} </div> ); } // *** `React.memo` will compare the props and skip the call if they're the same, reusing // the previous call's result. const FruitOption = React.memo(({ fruit, handleCheck }) => { console.log(`Rendering fruit ${fruit.id}`); return ( <div key={fruit.id}> <input checked={fruit.selected} onChange={(e) => handleCheck(e.target.checked, fruit.id)} type='checkbox' /> <label>{fruit.name}</label> </div> ); }); ReactDOM.render(<Example />, document.getElementById("root"));
 <div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>

如您所见,所有这些都到位后,只有更改后的水果被重新渲染。

React.memo :对于要求更复杂的组件,可以提供一个 function 作为第二个参数,用于判断两组 props 是否“相同”用于渲染目的。 默认情况下, React.memo只是做一个浅的相等比较,这通常就足够了。

暂无
暂无

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

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