douwebsp
douwebsp

Reputation: 23

How can i only re-render the new child component when mapping an array from Redux state?

Context

I have a Redux state in which i have an array of items. Everytime you click a link in the menu, an item gets added to this array.

In one of my components, which is connected to the store using connect(mapStateToProps)(Component) I map through this array and load the child components. These child components fetch data from an API.

Problem

Everytime a new item gets added to the array, all the child components re-render, because the array that it maps changes. I only want the newly added item to render and the rest persist as they are. I know you can do something with useMemo, but I'm not sure how to correctly use that with mapping.

Thanks to anyone who can help me!

Upvotes: 2

Views: 507

Answers (1)

HMR
HMR

Reputation: 39270

You can use pure component for each item:

const id = ((id) => () => id++)(1);

const Item = React.memo(function Item({ item, increase }) {
  const rendered = React.useRef(0);
  rendered.current++;
  return (
    <li>
      {item.id} - rendred {rendered.current} count:{' '}
      {item.count}
      <button onClick={() => increase(item.id)}>
        increase count
      </button>
    </li>
  );
});

const App = () => {
  const [items, setItems] = React.useState([]);
  const addItem = () =>
    setItems((items) => [{ id: id(), count: 0 }, ...items]);
  //use useCallback to create an increase function that
  //  never changes so you'll never pass a new increase
  //  function to item
  const increase = React.useCallback(
    //pass callback to state setter so items is not
    //  a dependency of the useCallback
    (id) =>
      setItems((currentItems) =>
        currentItems.map((item) =>
          item.id === id
            ? { ...item, count: item.count + 1 }
            : item
        )
      ),
    []
  );
  return (
    <div>
      <div>
        <button onClick={addItem}>+</button>
      </div>
      <div>
        {items.map((item) => (
          <Item
            key={item.id}
            item={item}
            increase={increase}
          />
        ))}
      </div>
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>


<div id="root"></div>

Upvotes: 1

Related Questions