Reputation: 23
Context
I have a Redux state in which i have an array of items. Everytime you click a link in the menu, an item gets added to this array.
In one of my components, which is connected to the store using connect(mapStateToProps)(Component)
I map through this array and load the child components. These child components fetch data from an API.
Problem
Everytime a new item gets added to the array, all the child components re-render, because the array that it maps changes. I only want the newly added item to render and the rest persist as they are. I know you can do something with useMemo, but I'm not sure how to correctly use that with mapping.
Thanks to anyone who can help me!
Upvotes: 2
Views: 507
Reputation: 39270
You can use pure component for each item:
const id = ((id) => () => id++)(1);
const Item = React.memo(function Item({ item, increase }) {
const rendered = React.useRef(0);
rendered.current++;
return (
<li>
{item.id} - rendred {rendered.current} count:{' '}
{item.count}
<button onClick={() => increase(item.id)}>
increase count
</button>
</li>
);
});
const App = () => {
const [items, setItems] = React.useState([]);
const addItem = () =>
setItems((items) => [{ id: id(), count: 0 }, ...items]);
//use useCallback to create an increase function that
// never changes so you'll never pass a new increase
// function to item
const increase = React.useCallback(
//pass callback to state setter so items is not
// a dependency of the useCallback
(id) =>
setItems((currentItems) =>
currentItems.map((item) =>
item.id === id
? { ...item, count: item.count + 1 }
: item
)
),
[]
);
return (
<div>
<div>
<button onClick={addItem}>+</button>
</div>
<div>
{items.map((item) => (
<Item
key={item.id}
item={item}
increase={increase}
/>
))}
</div>
</div>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Upvotes: 1