简体   繁体   English

关于 React.memo 和性能优化的问题

[英]The question about React.memo and performance optimisation

Suppose I have long list (let's assume there is no pagination yet) where each list item has input and ability to update own value (as a part of collection).假设我有很长的列表(假设还没有分页),其中每个列表项都有输入并能够更新自己的值(作为集合的一部分)。 Let's say code looks something like that:假设代码看起来像这样:

const initItems = [
    { id: 0, label: "Hello world" },
    ...
    { id: 100, label: "Goodby" }
];

function List() {
    const [items, setItems] = React.useState([...initItems]);

    const handleChange = React.useCallback((e, id) => {
        setItems(items.map(item => {
            if (item.id === id) {
                return {
                    ...item,
                    label: e.target.value
                }
            }

            return item;
        }));
    }, [items]);

    return (
        <ul>
            {items.map(({ id, label }) => {
                return (
                    <Item
                        id={id}
                        key={id}
                        label={label}
                        onChange={handleChange}
                    />
                )
            })}
        </ul>
    )
}

// Where Item component is:
const Item = React.memo(({ onChange, label, id }) => {
    console.log('Item render');

    return (
        <li>
            <input type="text" value={label} onChange={e => onChange(e, id)} />
        </li>
    )
});

Looks pretty straightforward, right?看起来很简单,对吧? While wrapping Item component with React.memo() what I wanted to achieve is to avoid re-render of each Item when some of the Item 's gets updated.在使用React.memo()包装Item组件时,我想要实现的是避免在某些Item更新时重新渲染每个Item Well, I'm not sure it should works with this strategy, since each Item is a part of collection ( items ) and when I update any Item then items gets mapped and updated.好吧,我不确定它是否适用于这种策略,因为每个Item都是集合( items )的一部分,当我更新任何Item时, items就会被映射和更新。 What I did try - is to write custom areEqual method for Item component, where I do comparison of label value from props:我所做的尝试是为Item组件编写自定义areEqual方法,我在其中比较道具中的label值:

function areEqual(prev, next) {
    return prev.label === next.label;
}

however with this approach the behaviour of updating items breaks down completely and updating next item reset previous updates and so on (I even could not observe any pattern to explain).但是,使用这种方法,更新项目的行为会完全崩溃,更新下一个项目会重置以前的更新等等(我什至无法观察到任何模式来解释)。

So the question: is it possible to avoid re-rendering of every item in such collection while having ability to update value of individual item?所以问题是:是否有可能避免重新渲染此类集合中的每个项目,同时能够更新单个项目的值?

Your problem here that you change callback on each render.您在这里的问题是您在每次渲染时更改回调。 So, you change callback, it changes onChange and this, in turn, runs rerender.因此,您更改回调,它会更改onChange ,而这又会运行重新渲染。 To avoid it you can use updater function with setState .为避免这种情况,您可以将更新程序 functionsetState一起使用。

const handleChange = React.useCallback((e, id) => {
  // I made separate function so it would be easier to read
  // You can just write `(items) =>` before your `items.map` and it will work
  function updater(items) {
    // we have freshest items here
    return items.map((item) => {
      if (item.id === id) {
        return {
          ...item,
          label: e.target.value,
        };
      }
      return item;
    });
  }
  // pass function
  setItems(upadter);
  // removed items from dependencies
}, []);

This way, your updater function will always get current value of state into parameters, and your props will update for actually updated item.这样,您的更新程序 function 将始终将 state 的当前值获取到参数中,并且您的道具将针对实际更新的项目进行更新。 Another solution would be to write custom updater that compares all values, but onChange .另一种解决方案是编写自定义更新器来比较所有值,但onChange This is ok in short term, but this can become complex and cumbersome to maintain.这在短期内是可以的,但这可能会变得复杂且难以维护。

Here is live example: https://codesandbox.io/s/unruffled-johnson-ubz1l这是现场示例: https://codesandbox.io/s/unruffled-johnson-ubz1l

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

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