On every click react rerender every item. How to avoid it? I want react to only render items that have changed. I tried using react memo and usecallback but it didn't help. I can't understand what is the reason. What are the ways to solve this problem? Thank you.
App.ts
import React, { useState } from "react";
import "./styles.css";
import ListItem from "./ListItem";
const items = [
{ id: 1, text: "items1" },
{ id: 2, text: "items2" },
{ id: 3, text: "items3" },
{ id: 4, text: "items4" },
{ id: 5, text: "items5" },
{ id: 6, text: "items6" },
{ id: 7, text: "items7" }
];
export default function App() {
const [activeIndex, setActiveIndex] = useState(1);
const onClick = (newActiveIndex: number) => {
setActiveIndex(newActiveIndex);
};
return (
<div className="App">
{items.map(({ id, text }) => (
<ListItem
key={id}
id={id}
text={text}
activeId={activeIndex}
onClick={onClick}
/>
))}
</div>
);
}
Item.ts
import React from "react";
interface ListItemProps {
id: number;
activeId: number;
text: string;
onClick: (newActiveIndex: number) => void;
}
const ListItem: React.FC<ListItemProps> = ({ id, activeId, text, onClick }) => {
const isActive = activeId === id;
const style = {
marginRight: "42px",
marginTop: "24px",
color: isActive ? "green" : "red"
};
console.log(`update id: ${id}`);
return (
<div>
<span style={style}>id: {id}</span>
<span style={style}>is active: {isActive ? "active" : "inactive"}</span>
<span style={style}>text: {text}</span>
<button onClick={() => onClick(id)}>setActive</button>
</div>
);
};
export default ListItem;
CodePudding user response:
activeIndex
changes with each click, so all of the components have new prop values and will need to re-render.
Instead of passing the activeIndex
to every component every time:
const ListItem: React.FC<ListItemProps> = ({ id, activeId, text, onClick }) => {
const isActive = activeId === id;
Pass an isActive
to each component:
const ListItem: React.FC<ListItemProps> = ({ id, isActive, text, onClick }) => {
Effectively moving the calculation of the bool
from the component to the consuming code:
<ListItem
key={id}
id={id}
text={text}
isActive={activeIndex === id}
onClick={onClick}
/>
Then memoization (coupled with useCallback
for the onClick
handler) should work because most components (the ones which aren't changing their "active" state) won't receive new props and won't need to re-render.
CodePudding user response:
Even with memoization it won't work, because your onClick
function changes your components state, and your children components depend on activeIndex
value, so every time you click you change the components state, and when state changes, the component re-renders itself and it's children, if your children didn't depend on your state, they wouldn't re-render (if you use memoization).
CodePudding user response:
Plus to David's answer i would return ListItem in React.memo React.memo(ListItem)