useCallback 依赖数组违背了更新数组时防止重新渲染的目的

发布于 2025-01-10 22:56:35 字数 1547 浏览 0 评论 0原文

以下 React 应用程序显示水果列表。每个 Fruit 都有一个“添加到收藏夹”按钮,该按钮使用 addFav 回调将所述水果添加到父项中的收藏夹列表中。传入 handleAddFav 回调会导致不必要的重新渲染,因此我将其包装在 useCallback 中,并将 Fruit 包装在 memo 中。

然而,useCallback 要求在其依赖项数组中包含 favs,这会导致 handleAddFav 在每次调用时都要重新计算。这违背了使用 useCallback 来停止重新渲染的目的,因为现在每次添加收藏夹时,每个 Fruit 都会重新渲染。我该如何解决这个问题?

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>
  );
}

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>
  );
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

旧情别恋 2025-01-17 22:56:35

一种方法是使用 setFavs 的函数版本,因此它不依赖于外部变量。

const handleAddFav = useCallback((title, id) => {
  setFavs(favs => [...favs, {title, id}])
}, [])

对于更一般的情况 - 即使您确实有一个必须重新计算的值,使用 useCallback 仍然可以减少 其他 值的情况的重新渲染组件发生变化,但计算值没有变化。例如,

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]);

如果 cb 被传递下来,即使它依赖于 numuseCallback 仍然会在只有 < code>toggled 发生更改,而 num 保持不变。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文