I have a list with a large amount of items inside. In order to improve rendering performance I decided to memoize single items. This is the code I used:
Parent component
const randomList = [];
for (let i = 0; i < 1000; i++) {
const randomInteger = Math.floor(Math.random() * 100);
randomList.push({ id: randomInteger, status: "" });
}
const List = () => {
const [list, setList] = useState(randomList);
const handleClick = (e) => {
const position = e.currentTarget.id;
const newList = list.map((item, idx) =>
`${idx}` === position ? { ...item, status: "red" } : item
);
setList(newList);
};
return (
<div>
{list.map(({ id, status }, idx) => {
return <Item id={id} idx={idx} status={status} onClick={handleClick} />;
})}
</div>
);
};
Item component
export const ChipTermStyled = styled(({ ...props }) => <Chip {...props} />)(
({ status = "" }) => {
return {
backgroundColor: status,
};
}
);
const Item = ({ id, idx, status, onClick }) => {
console.log(`item-${idx}`);
return (
<ChipTermStyled
id={`${idx}`}
key={`${id}-${idx}`}
label={`${id}-${idx}`}
status={status}
onClick={onClick}
/>
);
};
const arePropsEqual = (prevProps, newProps) =>
prevProps.id === newProps.id &&
prevProps.idx === newProps.idx &&
prevProps.status === newProps.status;
export default memo(Item, arePropsEqual);
This should render Item only when id, idx or status change. And it does the job. But! When I update the parent component list, it doesn't keep the state between item updates. See this:
I check the list before and after on the first click and they appear ok, but on the second click, the list lost all the previous item statuses.
Why can this be happening? What I'm doing wrong?
The problem was with how the state is being updated. For anyone that is facing this issue, instead of updating the state like this:
const handleClick = (e) => {
const position = e.currentTarget.id;
const newList = list.map((item, idx) =>
`${idx}` === position ? { ...item, status: "red" } : item
);
setList(newList);
};
You have to update it in the previous value of the state:
const handleClick = (e) => {
const position = e.currentTarget.id;
setList(prevList =>
prevList.map((item, idx) =>
${`idx`} === position
? { ...item, status: "red" }
: item
));
};