The following React app displays a list of fruits. Each Fruit
has a "add to fav" button which uses a addFav
callback to add said fruit to a list of favourites in the parent. Passing in the handleAddFav
callback causes unnecessary re-renders, so I wrapped it in a useCallback
and Fruit
in memo
.
However the useCallback
demands to have favs
in its dependency array which causes the handleAddFav
to be re-computed every time its called. This defeats the purpose of using useCallback to stop re-renders because now each Fruit
re-renders every time you add a favourite. How can I solve this?
import { useState, memo, useCallback } from "react";
import "./styles.css";
const Fruit = memo(({title, id, addFav}) => {
console.log(title, 'rendered')
return (
<div>
<div>{title}</div>
<button onClick={() => addFav(title, id)}>add fav</button>
</div>
)
})
export default function App() {
const [favs, setFavs] = useState([])
const data = [{title: 'apple', id: '1'}, {title:'orange', id:'2'}
, {title:'banana', id:'3'}]
const handleAddFav = useCallback((title, id) => {
setFavs([...favs, {title, id}])
}, [favs])
return (
<div className="App">
<h1>Testing useCallback that sets an array</h1>
<h2>Favorites</h2>
<button onClick={() => setFavs([])}>clear</button>
{
favs.map(({title, id}, i) => <span key={id i}>{title}</span>)
}
{
data.map(({title, id }) => (
<Fruit key={id} title={title} id={id} addFav={handleAddFav}/>
))
}
</div>
);
}
CodePudding user response:
One way is to use the function version of setFavs
instead, so it doesn't depend on an outer variable.
const handleAddFav = useCallback((title, id) => {
setFavs(favs => [...favs, {title, id}])
}, [])
For the more general situation - even if you did have a value that had to be re-computed, using useCallback
could still reduce re-renders for the cases in which other values in the component change, but not the computed value. For example
const TheComponent = () => {
const [toggled, setToggled] = useState(false);
const [num, setNum] = useState(5);
const cb = useCallback(() => {
// imagine that this does something that depends on num
}, [num]);
If cb
is passed down, even though it depends on num
, useCallback
will still prevent child re-renders in the case where only toggled
gets changed, and num
stays the same.