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> ); }
Advertisement
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> ); }