【问题标题】:Stale custom hook state property on callback回调时的陈旧自定义挂钩状态属性
【发布时间】:2023-02-15 16:32:40
【问题描述】:

我的 React 应用程序中有一个自定义挂钩,它公开了一个函数 (hookFn) 来计算 value。一旦更新了值(状态更改、触发 useEffect),挂钩就会通过回调函数提醒应用程序。问题是:在我的回调函数中,我希望能够通过hook.value 访问该值,但它似乎已经过时了!即使我知道值状态已更新!

代码沙盒:https://codesandbox.io/s/stoic-payne-bwp6j5?file=/src/App.js:0-910

import { useEffect, useRef, useState } from "react";

export default function App() {
  const hook = useCustomHook();

  useEffect(() => {
    hook.hookFn(hookCallback);
  }, []);

  function hookCallback(value) {
    console.log({givenValue: value, hookValue: hook.value});
  }

  return "See console for output";
}

function useCustomHook() {
  const callbackRef = useRef(null);
  const [value, setValue] = useState("initial value");

  useEffect(() => {
    if (callbackRef.current) {
      callbackRef.current(value);
    }
  }, [value]);

  function hookFn(callbackFn) {
    callbackRef.current = callbackFn;
    setValue("value set in hookFn");
  }

  return { hookFn, value };
}

仅供参考:在我的实际应用程序中,钩子用于搜索,随着更多搜索结果可用,它可能会多次调用回调函数。

有什么方法可以确保hook.value 有效吗?或者一般来说,钩子公开状态变量是不好的做法吗?

【问题讨论】:

  • 您是否必须在实际应用中使用setInterval?还是仅举个例子?
  • @Jae 是的——这是一个可能很昂贵的搜索功能,所以我希望它异步运行。在应用程序的最终版本中,它可能会同时搜索本地缓存和 ajax 获取的数据,但我仍然可能会使用 setTimeout,因此它从一开始就异步运行。这听起来像是不好的做法吗?

标签: reactjs react-hooks


【解决方案1】:

结果 hook.value 过时了,因为当我从 hookCallback 访问它时,hook 过时了。每次在我的自定义挂钩中发生状态更改时,useCustomHook 都会生成一个新对象。

那么,复杂的解决方案是为hook创建一个ref,并在useEffect中保持更新。但是我必须确保在访问hookRef.current.value之前等待useEffect运行...这是我尝试完成这项工作的尝试:https://codesandbox.io/s/dazzling-shirley-0r7k47?file=/src/App.js

但是,更好的解决方案:不要混合 React 状态和手动回调。相反,只需观察 useEffect 中的状态变化,如下所示:

import { useEffect, useState } from "react";

export default function App() {
  const hook = useCustomHook();

  useEffect(() => {
    hook.hookFn();
  }, []);

  useEffect(() => {
    if (hook.value) console.log({ hookValue: hook.value });
  }, [hook.value]);

  return "See console for output";
}

function useCustomHook() {
  const [value, setValue] = useState("initial value");

  function hookFn(callbackFn) {
    setValue("value set in hookFn");
  }

  return { hookFn, value };
}

请注意代码已简化,无需担心状态不同步。

【讨论】:

    【解决方案2】:

    我认为您几乎已经回答了自己的问题。或者,您可以将回调函数作为输入传递给自定义挂钩。

    import { useEffect, useState } from "react";
    import "./styles.css";
    
    export default function App() {
      const hook = useCustomHook(hookCallback);
    
      useEffect(() => {
        hook.setNewValue();
      },[])
    
      function hookCallback(value) {
        console.log({
          givenValue: value,
          hookValue: hook.value, // Why is this stale??
          areIdentical: value === hook.value // Should be true!!!
        });
      }
    
      return <h1>See console for output</h1>;
    }
    
    function useCustomHook(callback) {
      const [value, setValue] = useState("initial value");
    
      useEffect(() => {
          callback(value);
      }, [value]);
    
      function setNewValue(callbackFn) {
        setValue("value set in hookFn");
    
        setTimeout(() => {
          setValue("value set in setTimeout");
        }, 100);
      }
    
      return { setNewValue, value };
    }

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-12-07
      • 2020-01-23
      • 1970-01-01
      • 2016-07-05
      • 1970-01-01
      • 2021-09-28
      • 2021-06-24
      • 2019-11-08
      相关资源
      最近更新 更多