【问题标题】:useState() not updating inside of useEffect() hookuseState() 没有在 useEffect() 钩子内部更新
【发布时间】:2021-10-24 08:24:08
【问题描述】:

我在更新 useEffect() 中的 useState() 时遇到问题。我想我可能会陷入无限循环,并且无论我传入的布尔值如何,useState 都不会更新。它总是显示为 false。

export const useAlert = () => {
  const action = useSelector(state => state.edit.action);

  const dispatch = useDispatch();

  const booleanA = true;

  const booleanB = false;

  const [isOverMax, setIsOverMax] = useState(false);
  const reset = () => {
    dispatch(resetEditAction());
  };

  useEffect(() => {
    setIsOverMax(booleanA || booleanB);
  }, isOverMax);

  return [isOverMax, reset];
};

关于更多上下文,booleanAbooleanB 是使用其他代码动态生成的,但即使它们像上面那样被硬编码,isOverMax 也只有false。即使两个布尔值都被硬编码为真,我也永远无法获得真值。

【问题讨论】:

  • 首先确保写useEffect(() => { setIsOverMax(booleanA || booleanB); }, [isOverMax]);其次,我会小心更新useEffect中的状态可能会出现一些意想不到的副作用
  • 免责声明:我是 JS 菜鸟,所以......你试过在 useEffect 中写 console.log(isOverMax) 吗?因为 JS 是单线程的,所以你有可能马上就看到了这个函数的返回值。无论在何处调用它,您都可以运行一个将值记录到控制台的间隔。它可以设置为 true,但由于操作队列,您从未注意到。顺便说一句 +1 德鲁里斯的评论。您的 IDE 可能会为您指出这一点;如果没有,那么你可以使用 React/Vue 插件/预设运行 eslint,它会告诉你应该拥有什么。
  • 你的useEffect 依赖不正确,应该是一个数组。此外,您不希望 useEffect 回调更新该数组中的任何依赖项,即如果调用 setIsOverMaxisOverMax 不应该是没有对状态更新程序进行条件检查的依赖项。我的猜测是您的依赖项应该是[booleanA, booleanB],因为这些是效果回调中引用的内容。您能否更新您的问题以包含更准确的代码表示?
  • @HansKrohn 经过更多学习,我认为空的依赖数组比更新使用效果挂钩内的状态更可取。这可能会导致无限循环和渲染问题。并且具有正确值的填充依赖数组将是一个更好的选择。
  • @DrewReese 感谢您指出错误的依赖数组。

标签: reactjs react-redux react-hooks state


【解决方案1】:

状态isOverMax 派生自booleanAbooleanB。因此,isOverMax 应该在 booleanAbooleanB 更改时更改。您应该在依赖项数组中反映这一点。

 useEffect(() => {
   setIsOverMax(booleanA || booleanB);
 }, [booleanA, booleanB]);

这是一个工作示例。只需点击“显示代码 sn-p”,然后点击“运行代码片段”

const useAlert = () => {
  const [booleanA, setA] = React.useState(true);
  const [booleanB, setB] = React.useState(false);
  const [isOverMax, setIsOverMax] =React. useState(false);

  const reset = () => {
    setA(false);
    setB(false);
  };

  React.useEffect(() => {
    setIsOverMax(booleanA || booleanB);
  }, [booleanA , booleanB]);

  // just for testing
  const toggleA = () => {
    setA((a) => !a);
  }

   return [isOverMax, reset, toggleA];
};

const App = () => {
  const [alert, reset, toggleA]  = useAlert();
  return <div>
    isOverMax={"" + alert}<br/>
    <button onClick={() => toggleA()} >Toggle A</button>
    <button onClick={() => reset()} >Reset </button>
  </div>;
};

ReactDOM.render(<App />, document.getElementById("root"));
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>

由于booleanAbooleanB 状态已经存在,当其中一个状态发生变化时,它们将触发重新渲染。因此你可以只返回isOverMax的计算结果

const useAlert = () => {

   // ...
   return [booleanA || booleanB, reset];
}

    const useAlert = () => {
      const [booleanA, setA] = React.useState(true);
      const [booleanB, setB] = React.useState(false);
      const isOverMax = booleanA || booleanB;

      const reset = () => {
        setA(false);
        setB(false);
      };

      // just for testing
      const toggleA = () => {
        setA((a) => !a);
      }

       return [isOverMax, reset, toggleA];
    };

    const App = () => {
      const [alert, reset, toggleA]  = useAlert();
      return <div>
        isOverMax={"" + alert}<br/>
        <button onClick={() => toggleA()} >Toggle A</button>
        <button onClick={() => reset()} >Reset </button>
      </div>;
    };

    ReactDOM.render(<App />, document.getElementById("root"));
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>

编辑

对于何时不使用效果挂钩,是否有任何一般准则,这样我就不会开始将它扔到不重要的代码中?

不是真的,但你应该读过Rules of Hooks。规则告诉你应该做什么来防止不可预知的行为。可悲的是,他们没有解释该规则的原因,这个问题是解释这一切的错误地方。因此,如果您还有问题,请打开另一个问题。

【讨论】:

  • 这是一个非常彻底的答案,有两种不同的解决方案解决了我的问题。它有助于澄清依赖关系数组以及它的一般功能。我也喜欢第二个解决方案,它通常避免使用useEffect。是否有关于何时不使用效果钩子的一般准则,这样我就不会开始将它扔到不重要的代码中?
【解决方案2】:

删除useEffect的依赖项中的isOverMax以避免无限循环

useEffect(() => {
  setIsOverMax(booleanA || booleanB);
}, []);

【讨论】:

    猜你喜欢
    • 2020-09-02
    • 2021-01-27
    • 1970-01-01
    • 1970-01-01
    • 2022-12-08
    • 2020-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多