0

When i select item all items are re-rendered. How can I make it render only the one I change. Only state that is changing is 'selected'. Items are always same. So how can i make that only one item is gonna re-rendered base on 'selected' state. Im passing in Item component 'selected' state and i think there is problem that i dont get it.

import { memo, useState } from "react";

const items = Array(100)
  .fill(null)
  .map((item, index) => {
    return { ...item, id: crypto.randomUUID(), name: `hello - ${index + 1}` };
  });

const App = () => {
  const [selected, setSelected] = useState<string[]>([]);

  const selectHandler = (isIncluded: boolean, id: string) => {
    if (isIncluded) {
      setSelected(prev => prev.filter(idx => idx !== id));
    } else {
      setSelected(prev => [...prev, id]);
    }
  };

  const MemoizedItem = memo(Item);

  return (
    <div className="flex flex-col gap-5 items-center justify-center py-10">
      {items.map(item => (
        <MemoizedItem
          key={item.id}
          data={item}
          selected={selected}
          selectHandler={selectHandler}
        />
      ))}
    </div>
  );
};

export default App;

type PropsItem = {
  data: { id: string; name: string };
  selected: string[];
  selectHandler: (isSelected: boolean, id: string) => void;
};

const Item = ({ data, selected, selectHandler }: PropsItem) => {
  return (
    <div className="cursor-pointer min-w-[170px] text-center p-2">
      {selected.includes(data.id) ? (
        <s className="text-2xl" onClick={() => selectHandler(true, data.id)}>
          {data.name}
        </s>
      ) : (
        <p className="text-2xl" onClick={() => selectHandler(false, data.id)}>
          {data.name}
        </p>
      )}
    </div>
  );
};

I tried using memo and useCallback but in this case is not working. Im clueless. Im learning and still dont get it.

Konrad
  • 21,590
  • 4
  • 28
  • 64
  • 1
    The `memo` should be here `const Item = memo(({ data, selected, selectHandler }: PropsItem) => {`. Now you are creating a new component in every render – Konrad Feb 01 '23 at 21:46
  • Instead of passing the "selected" array, try to pass a "isSelected" boolean, otherwise you're passing a new array reference each time the selection changes, which triggers a re-render of all items. – Anton Podolsky Feb 01 '23 at 21:50
  • 1
    Finally its working. I did what @Konrad said, i wrapped Item component in memo exactly as he said, and i passed 'isSelected' boolean in Item component instead of 'selected' state as Anton said. And last thing what i did was i wrapped 'selectHandler' function in useCallback so it renders only once. Thank you for your advice guys and have a nice day. :) – Mario Podolinský Feb 01 '23 at 22:21
  • I think that `useCallback` makes no difference here – Konrad Feb 01 '23 at 22:39
  • 1
    Without callback all items are re-rendered. Atleast that what i see, im using react developer tools and when i increase array of items to 5000 its lagging. With callback is instant. Its probably because i passing 'selectHandler' function in Item component and in every render is function re-creating and thats why we need to wrap in it. But im not sure. @Konrad – Mario Podolinský Feb 01 '23 at 23:04
  • Yes, makes sense, sorry :) – Konrad Feb 01 '23 at 23:12
  • Now try it without memo. I suspect it might work the same. – Anton Podolsky Feb 01 '23 at 23:31
  • Its not laggy as before but still it re-render every item and react devtools says thats because of parent component is re-rendered. – Mario Podolinský Feb 01 '23 at 23:41

0 Answers0