【问题标题】:How to deal with exhaustive-deps when a required dependency must not trigger a call on update?当所需的依赖项不能触发更新调用时,如何处理穷举?
【发布时间】:2022-08-15 01:56:42
【问题描述】:

在我的功能性 React 组件中,我有一个在组件挂载和某些子事件中都使用的函数:

const [count, setCount] = useState(0);
  
const myFunction = useCallback(() => {
    // do something with count, such as a fetch or console.log for example
}, [])

useEffect(() => {   
    myFunction()
}, [myFunction])

return (
    <button onClick={myFunction}>
        myButton
    </button>
)

myFunction 必须在挂载和单击按钮时调用。然而,经典的 React 方法意味着将 count 声明为 useCallback 中的依赖项,这将在每次 count 被程序的另一部分更新时触发 myFunction 的调用,我不这样做想。 count 必须能够在不触发 myFunction 的情况下更新。

我找不到这样做的干净解决方案。有吗?还是我应该以不同的方式编写代码?处理这种情况的最佳方法是什么?

    标签: reactjs react-hooks use-effect


    【解决方案1】:

    对于您的具体示例,有一个简单的解决方案。与其在useCallback 中使用count 状态变量并因此使其成为useCallback 挂钩的依赖项,不如使用状态更新器函数的回调参数。所以而不是:

    const myFunction = useCallback(() => {
        setCount(count + 1)
    }, [])
    

    你可以这样写:

    const myFunction = useCallback(() => {
        setCount((prevCount) => prevCount + 1)
    }, [])
    

    对于更复杂的实例,另一种方法是使用useReducer 挂钩而不是useState。由于您将使用useReducer 的dispatch 函数来更新状态,因此不需要将state 作为useCallback 挂钩中的依赖项。

    const [count, dispatch] = useReducer((state, action) => {
      switch (action.type) {
        case "INCREMENT":
          return state + action.by;
    
        default:
          return state;
      }
    }, 0);
    
    const myFunction = useCallback(() => {
      dispatch({ type: "INCREMENT", by: 1 });
    }, []);
    
    useEffect(() => {
      myFunction();
    }, [myFunction]);
    
    return (
      <div>
        <p>{count}</p>
        <button onClick={myFunction}>myButton</button>
      </div>
    );
    

    【讨论】:

    • 这些是有趣的解决方案,但是它们包括从函数中删除所有对 count 的引用。如果我需要对函数中的某些业务逻辑使用 count 怎么办?我为这篇 SO 帖子简化了我的功能,但我简化了太多,我将在代码示例中添加这个特殊性。
    • @papillon 如果您需要将count 用于某些业务逻辑,那么您可以在reducer 内部使用它,然后从myFunction 发送将更新count 状态的操作。
    【解决方案2】:

    将 React functional updates 用于 setState:

    setCount(count => count + 1);
    

    这样您就不需要将count 添加到依赖项中。

    【讨论】:

    • 这是一个有趣的解决方案,但是它包括从函数中删除所有对 count 的引用。如果我需要对函数中的某些业务逻辑使用 count 怎么办?我为这篇 SO 帖子简化了我的功能,但我简化了太多,我将在代码示例中添加这个特殊性。
    • 您可能可以重构代码以防止这种情况发生。但如果它仍然导致问题,您可以使用useRef 作为实例值并确保您的函数只运行一次。 See here。请注意,refs 是对象,您不必将它们包含在依赖项数组中,因为它们不会改变。此外,如果它们的值 (ref.current) 发生变化,它们也不会导致渲染。
    • 不知道如何重构我的代码。我正在构建一个可以通过单击并拖动它来移动视图的地图,例如谷歌地图。某个变量view 对应于地图的中心。单击+拖动地图时,它会更新以相应地移动地图。根据它的新位置,我在地图上获取与这个新位置相对应的数据。但是,我不希望每次鼠标移动时都进行获取,这太频繁了。我希望仅在释放点击时进行提取。当view 发生变化时,不得调用获取世界数据的函数。
    • 1. 可以使用mouseup event,而不是在useEffect中获取数据。 2. 你可以去抖动你的 fetch 请求。
    • 1. 在mousup 事件上调用你的函数。 2. 在useEffect 中调用它,但使用 ref 跟踪您的函数是否已被调用。 Something like this link。根据 ref 有条件地调用您的函数。 3. 重构代码的示例是:不要从函数中的状态读取center 变量,而是将其作为参数传递,这样您的useEffect 将与view 状态解耦。如果没有您的代码的最小可复制示例,我无法提供更多帮助。
    【解决方案3】:

    截至 2022 年 8 月,这实际上恰好是框架设计中的一个缺陷。我在互联网上浏览了很多帖子,但没有找到任何解决方案。我发现的唯一重要信息是人们抱怨这种机制不适合许多合法用例。我想这个问题并没有真正的标准解决方案。就个人而言,我决定在我的项目中全局禁用警告,因为它是痛苦的,而不是有用的。

    我不会将我的答案标记为“已接受”,因为我认为它不满意,但是对于将阅读此内容的人,我建议您查看此帖子中的其他答案。它们不能解决问题,但会提供可能适合您需要的有用技巧。

    【讨论】:

      猜你喜欢
      • 2019-05-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-03
      • 1970-01-01
      • 1970-01-01
      • 2021-09-29
      • 1970-01-01
      相关资源
      最近更新 更多