reactjsperformancereact-memo

Avoiding re-render unchanged items


I'm trying to improve the performance of a long list with changeable items using React.memo, but I'm getting strange behavior.

Steps to reproduce:

1 - Go to the sandbox here

2 - Press add on the first item

3 - Press add on the second item

import React, { useState } from "react";

const App = () => {
  const [list, setList] = useState([
    { id: 1, qtd: 0 },
    { id: 2, qtd: 0 },
    { id: 3, qtd: 0 },
    { id: 4, qtd: 0 }
  ]);

  const onAdd = (id, qtd) => {
    setList(
      list.map((item) => {
        if (item.id === id) {
          return { ...item, qtd };
        } else {
          return item;
        }
      })
    );
  };

  return (
    <ul>
      {list.map((item) => (
        <Item {...item} onChange={onAdd} />
      ))}
    </ul>
  );
};

const Item = React.memo(({ id, qtd, onChange }) => {
  console.log("Rendering -> " + id);

  return (
    <li>
      <span>{qtd}</span>
      <button onClick={() => onChange(id, qtd + 1)}>Add</button>
    </li>
  );
}, areEqual);

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

export default App;

Solution

  • Solved it by using the latest state of list

    const App = () => {
      const [list, setList] = useState([
        { id: 1, qtd: 0 },
        { id: 2, qtd: 0 },
        { id: 3, qtd: 0 },
        { id: 4, qtd: 0 }
      ]);
    
      const onAdd = (id, qtd) => {
        setList((l) =>
          l.map((item) => {
            if (item.id === id) {
              return { ...item, qtd };
            } else {
              return item;
            }
          })
        );
      };
    
      return (
        <ul>
          {list.map((item) => (
            <Item key={item.id} {...item} onChange={onAdd} />
          ))}
        </ul>
      );
    };
    

    Also added key as stated by Ngọc Hy

    https://codesandbox.io/s/flamboyant-grothendieck-z8uxf?file=/src/App.js