Why is React rerendering my unchanged list items? How can I prevent this?

I’m trying to get my head around the way React renders list items.

I have boiled my question down to some very simple code (shown below) (sandbox here). It’s just a list of 3 items and a button that appends 3 more items to the end of this list.

I want to prevent React from rerendering the first three items when the extra items are added. Initially I thought this was done automatically if I’ve set my “keys” properly, which I believe I have. This didn’t work so I tried wrapping the list component in React.memo. But the console shows that I’m still rerendering 6 items when I expect to only be rendering the 3 extra items.

Why is this? I feel like possibly it’s something to do with me mutating the arr array that contains the item when I set the state with setArr, and perhaps there is a method to prevent this. But I’m at a loss of what it is. What am I doing wrong?

Thanks in advance.

import React, { memo, useState } from "react";

export default function App() {
  const [arr, setArr] = useState(["a", "b", "c"]);

  const addItem = () => {
    const extraItems = ["d", "e", "f"];
    setArr((arr) => [...arr, ...extraItems]);
  };

  const SimpleComponent = memo(({ text }) => {
    console.log("Rendered ", text);
    return <li>{text}</li>;
  }, true);

  return (
    <div className="App">
      <ul>
        {arr.map((item) => {
          return <SimpleComponent key={item} text={item} />;
        })}
      </ul>

      <button onClick={() => addItem()}>Add more</button>
    </div>
  );
}

Answer

The problem is your memo is inside the component, so it gets re-created on each render, which makes it all pointless, the momoized component needs to be outside the component that uses it, try

const SimpleComponent = memo(({ text }) => {
  console.log("Rendered ", text);
  return <li>{text}</li>;
});

export default function App() {
  const [arr, setArr] = useState(["a","b","c"]);

  const addItem = () => {
    const extraItems = ["d", "e", "f"];
    setArr((arr) => [...arr, ...extraItems]);
  };

  return (
    <div className="App">
      <ul>
        {arr.map((item) => {
          return <SimpleComponent key={item} text={item} />;
        })}
      </ul>

      <button onClick={() => addItem()}>Add more</button>
    </div>
  );
}

Leave a Reply

Your email address will not be published. Required fields are marked *