memoize react component callback in a list

240 Views Asked by At

How can I memoize a callback generated in the map loop? This (obviously) gives an error:

const SomeComponent = ({ items }: { items: ItemData[] }) => {
  const getItemCallback = (item: ItemData) => (e: React.MouseEvent) => {
    e.preventDefault();

    //do something with item
  }

  return <div>
    {items.map(item => {
      const callback = useCallback(getItemCallback(item));
    
      return <Item title={item.title} itemCallback={callback} />
    })}

  </div>
}

enter image description here

3

There are 3 best solutions below

2
Ori Drori On BEST ANSWER

Wrap getItemCallback in useCallback and pass it to the component:

const SomeComponent = ({ items }: { items: ItemData[] }) => {
  const getItemCallback = useCallback((item: ItemData) => (e: React.MouseEvent) => {
    e.preventDefault();

    //do something with item
  }, []);

  return <div>
    {items.map(item => (
      <Item 
        key={item.key}
        item={item} 
        itemCallback={getItemCallback} 
        />
    ))}
  </div>
}

The component then calls the callback, and passes the item to create a new function wrapped with useCallback:

const Item = ({ item, itemCallback }) => {
  const callback = useCallback(itemCallback(item), []);
  
  return (
    ...
  );
}
2
nikhil98 On

const ItemList = ({ items }: { items: ItemData[] }) => {
  const getItemCallback = useCallback((item: ItemData) => (e: React.MouseEvent) => {
    e.preventDefault();

    //do something with item
  }, []);

  return (
    <div>
      {items.map(item => (
        <Item title={item.title} itemCallback={getItemCallback(item)} />
      ))}
    </div>
  );
};

const SomeComponent = ({ items }: { items: ItemData[] }) => {
  return <ItemList items={items} />;
};

0
nikhil98 On

The a updated version of your code using

const SomeComponent = ({ items }: { items: ItemData[] }) => {
      const getItemCallback = (item: ItemData) => (e: React.MouseEvent) => {
        e.preventDefault();
    
        //do something with item
      }
    
      return (
        <div>
          {items.map(item => {
            const callback = useMemo(() => getItemCallback(item), [item]);
            return <Item title={item.title} itemCallback={callback} />;
          })}
        </div>
      );
    };