reactjsreact-hooksreact-stateusecallbackmemo

React component re-rendering even after using memo and useCallback


The entire list gets re-rendered instead of the particular selected list item upon state change

https://codesandbox.io/s/lfgxe (Refer the console to see components that gets rendered)

When the "add to the main array" button is clicked, the array(state) has to get updated and only the particular list item has to be re-rendered. But all the items in the list are re-rendered. I tried passing key prop, memo, callbacks they but didn't work.

Links that I referred to:

  1. https://alexsidorenko.com/blog/react-list-rerender/

  2. https://dmitripavlutin.com/dont-overuse-react-usecallback/

App.js:


export default function App() {
  const [value5, setValue5] = useState([]);
  let a = [1, 2, 3, 4, 5, 6];
  console.log("===========Parent component called ======================");

  let buttonClick = useCallback((keyID) => {
    setValue5((c) => [...c, keyID]);
  }, []);
  console.log(value5);
  return (
    <div className="App">
      {a.map((i) => {
        return (
          <MapperComp buttonClick={buttonClick} keyID={i} key={i.toString()}>
            <h1>{i} from the app.js</h1>
            <h1>{i} from the app.js</h1>
          </MapperComp>
        );
      })}
    </div>
  );
}

MapperComp.js:

import React, { memo } from "react";

const MapperComp = memo(({ buttonClick, keyID, ...props }) => {
  console.log("component", keyID);

  return (
    <div>
      <div>
        <h1>{keyID}</h1>
        {props.children}
        <button
          onClick={() => {
            buttonClick(keyID);
          }}
        >
          Add to the main array
        </button>
      </div>
    </div>
  );
});

export default MapperComp;


Solution

  • You have used useCallback for buttonClick that will not redefine the function. But setValues will update values state and re-render the whole component. For solving it, you should wrap this list in useMemo adding a in the dependency array.

    const list = useMemo(() => {
       return (
          a.map((i) => {
            return (
              <MapperComp buttonClick={buttonClick} keyID={i} key={i.toString()}>
                <h1>{i} from the app.js</h1>
                <h1>{i} from the app.js</h1>
              </MapperComp>
            );
          }
       );
    }, [a])