Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Memoize child component when updating parent array of objects state

I’m trying to memoize my ItemCard component, so when I call setItems(), only the ItemCard being updated gets rerendered, is this possible?

I have a super simple component like:

const ItemList = () => {
  const [items, setItems] = useState([
    { id: 1, count: 5 },
    { id: 2, count: 7 },
    { id: 3, count: 2 },
  ]);

const updateItemCount = (itemId, add) => {
  const updatedItems = items.map((item) => {
    const { id, count } = item;

    if (id === itemId) {
      return { ...item, count: add ? count + 1 : count - 1 };
    }
    return item;
  });

  setItems(updatedItems);
};

  return (
    <div>
      {items.map((item) => (
        <ItemCard {...item} updateItemCount={updateItemCount} />
      ))}
    </div>
  );
};

And ItemCard looks like

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

import { memo } from 'react';

const ItemCard = memo(({id, count, updateItemCount}) => {

console.log("re-rendered", id)

  return (
    <button onClick={() => updateItemCount(id, true)}></button>
  );
});

The issue is when I log re-renders, they all seem to re-render after the button click

>Solution :

You need to wrap updateItemCount in useCallback, since it’s re-created on each render, and the memo sees a changed prop. In addition, add a key to each item you render in the map.

If we’ll pass the items array to the useCallback dependencies, the function would be again re-created whenever the items changes. To avoid that use an updater function when you call setItems.

const { useState, useCallback, memo } = React;

const ItemList = () => {
  const [items, setItems] = useState(() => [
    { id: 1, count: 5 },
    { id: 2, count: 7 },
    { id: 3, count: 2 },
  ]);
  
  const updateItemCount = useCallback((itemId, add) => {
    setItems(items => items.map(item => {
      const { id, count } = item;

      if (id === itemId) {
        return { ...item, count: add ? count + 1 : count - 1 };
      }
      return item;
    }));
  }, []);

  return (
    <div>
      {items.map((item) => (
        <ItemCard key={item.id} {...item} updateItemCount={updateItemCount} />
      ))}
    </div>
  );
};

const ItemCard = memo(({id, count, updateItemCount}) => {
  console.log(`id update: ${id} - count: ${count}`);
  return (
    <button onClick={() => updateItemCount(id, true)}>{count}</button>
  );
});

ReactDOM
  .createRoot(root)
  .render(<ItemList />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>

<div id="root"></div>
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading