Home > Back-end >  useCallback dependency array defeats the purpose of preventing re-renders when updating an array
useCallback dependency array defeats the purpose of preventing re-renders when updating an array

Time:02-28

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.

  • Related