How to efficiently render arrays?

So I’ve completed the section “Global State Management” in the Ultimate React Course 2, and one thing I’m just not getting is how to effeciently render dynamic arrays?

Let’s say I have a very simple state object containing an array of strings:

const [items, setItems] = useState(["Foo", "Bar", "Baz"]);

And I’m rendering these with a MyComponent:

{items.map((item) => (<MyComponent text={item} /> ))}

Nothing terribly complicated about MyComponent, it’s just accepting the text as a prop and rendering it with a div along with a console log so that I can see when the render occurs:

interface Props {
  text: string;
}

export default ({ text }: Props) => {
  console.info(`Rendering component: ${text}`);
  return <div>{text}</div>;
};

Here’s the thing: how do I update any of the array items without causing the whole thing to re-render? According to Mosh’s video, I need to replace the entire list whenever any list element (or its properties) change:

const setItem = (itemIndex: number, newItem: string) => {
  setItems(
    items.map((item, index) => {
      return index == itemIndex ? newItem : item;
    })
  );
};

When I do this, the console shows that every single MyCompont in the list is re-rendered on account of the parent change propagating down to all of them. I’ve tried Memo’izing both the list and the text props but the result was the same.

This is a very simple example, but I can imagine it getting out of hand very quickly. I’m pretty sure the Facebook app, for example, doesn’t re-render every post on the page every single time the Like count for any of those posts changes! I can see how a change in a child might propagate across an entire page if, say, it affects layout, but that’s something I would expect to ordinarily be handled by the CSS?

Am I missing something obvious here?

Did you try to set the key prop on your MyComponent?

Good point, thanks! But yes, setting the key in MyComponent causes the same behaviour, as does wrapping MyComponent in another element (div etc) and setting the key there.

Got some help from Reddit, if you memoize components then they’ll only re-render when their props change:

const MyComponent = memo(   // <-- right here
    ({ text }: Props) => {
        console.info(`Rendering component: ${text}`);
        return <div>{text}</div>;
    });

export default MyComponent;

With all due respect to Mosh, I really think this should have been covered in the Global State Management section of React Intermediate Topics. I can envisage performance being utterly destroyed in any real-world application that doesn’t use it.

More info in the React documentation for memo.

Well, you can try below code it may resolve your error.

import React, { useState, useCallback } from 'react';

interface Props {
  text: string;
}

const MyComponent = React.memo(({ text }: Props) => {
  console.info(`Rendering component: ${text}`);
  return <div>{text}</div>;
});

const App = () => {
  const [items, setItems] = useState(["Foo", "Bar", "Baz"]);

  const setItem = useCallback((itemIndex: number, newItem: string) => {
    setItems(prevItems =>
      prevItems.map((item, index) =>
        index === itemIndex ? newItem : item
      )
    );
  }, []);

  return (
    <div>
      {items.map((item, index) => (
        <MyComponent key={index} text={item} />
      ))}
      <button onClick={() => setItem(0, "New Foo")}>Change Foo</button>
    </div>
  );
};

export default App;

Thanks