【问题标题】:React hooks function dependencyReact hooks 函数依赖
【发布时间】:2020-04-03 22:45:23
【问题描述】:

我发现自己处于一个奇怪的境地。我正在实现一个钩子,但我无法实现我想要的。

我有这样的事情:

const appHook = props => {
  const [foo, setFoo] = React.useState([]);
  const [bar, setBar] = React.useState([]);

  React.useEffect(() => {
    setFoo(getFoo(props.fooList, props.fooId));
    setBar(getBar(foo.listToFilter));
  }, [props.fooId]);

  const getCurrentBlockTrade = (arrayToFilter, number) =>
    arrayToFilter.filter(array => array.id === number);

  const getSubOutList = (...) => {
   ...
  };

return (<div>something</div>)
}

我的问题是函数 setFoo 被正确执行,所以 foo state 是一个新数组,但是依赖于 foo 状态的 setBar 接收一个空数组。基本上 setBar 在 setFoo 完成之前执行,因此 getBar 函数接收一个空数组。

管理这种依赖的正确方法是什么?

谢谢, F.

【问题讨论】:

    标签: javascript reactjs react-hooks


    【解决方案1】:

    setState 是一个异步函数,这就是为什么您在setBar 函数中接收到一个空数组的原因。基本上你不能确定状态会在第二个setState 评估之前更新。

    为什么不在这两种情况下都简单地引用道具?

    React.useEffect(() => {
       const newFoo = getFoo(props.fooList, props.fooId);
    
       setFoo(newFoo);
       setBar(getBar(newFoo.listToFilter));
    }, [props.fooId]);
    

    【讨论】:

      【解决方案2】:

      设置状态是一个异步过程。所以setBar(getBar(foo.listToFilter)); 调用这个 foo 是空数组。您可以为此使用另一个 useEffect

       React.useEffect(() => {
          setFoo(getFoo(props.fooList, props.fooId));
        }, [props.fooId]);
      
      
      
       React.useEffect(() => {
          setBar(getBar(foo.listToFilter));
       }, [foo]);
      

      【讨论】:

      • 谢谢,如果不是绝对必要的话,我会避免多次使用 useEffect 但这是有道理的!
      【解决方案3】:

      TL;DR;您的解决方案很可能是kind user's answer

      下面我将描述我迄今为止在整个研究过程中的想法和学到的东西,并从人们、博客、...中提出 5 条建议/解决方案...


      你说过:

      我的问题是函数 setFoo 被正确执行,所以 foo state 是一个新数组,但是依赖于 foo 状态的 setBar 接收一个空数组。基本上 setBar 在 setFoo 完成之前执行,因此 getBar 函数接收一个空数组

      你说得对。基本上是因为在 React(包括 Hooks 和类组件)中,setState 是异步的。这是什么意思?这意味着 setSomething 只是告诉 React 稍后重新渲染组件。它不会神奇地替换当前运行函数中的const something 变量——这是不可能的。

      const [foo, setFoo] = useState(0)
      
      function handleClick() {
        setFoo(42) 
        // we declared foo with const, you "obviously" shouldn't expect this 
        // to "somehow" immediately change `foo` to 42
      
        console.log(foo); 
        // it's 0 in this render, BUT on next render, `foo` will be 42
      }
      

      解决方案 1。

      对您来说最简单的技术是将新计算的 foo 值存储在一个变量中,然后将新计算的值用于 setFoo 和 setBar - 这是一种非常流行的技术。

      React.useEffect(() => {
         const newFoo = getFoo(props.fooList, props.fooId);
      
         setFoo(newFoo);
         setBar(getBar(newFoo.listToFilter));
      }, [props.fooId]);
      
      // or: shouldn't use this, only to demonstrate the callback syntax in 
      // the new setState Hook (different than the old callback syntax setState):
      React.useEffect(() => {
         setFoo(() => {
             const newFoo = getFoo(props.fooList, props.fooId);
             setBar(getBar(newFoo.listToFilter));
             return newFoo;
         })
      }, [props.fooId]);
      

      解决方案 2。

      可以在这里找到另一种技术:https://stackoverflow.com/a/54120692/9787887 正在使用 useEffectsetBarfoo 的依赖关系列表。

      React.useEffect(() => {
          setFoo(getFoo(props.fooList, props.fooId));
      }, [props.fooId]);
      
      React.useEffect(() => {
          setBar(getBar(foo.listToFilter));
      }, [foo]);
      

      尽管答案获得了 27 票,但我认为这只是使情况过于复杂,而且(据我所知)应该避免使组件不必要地重新渲染 2 次​​strong>而不是 1 次。


      解决方案 3。

      另一个可能可行的解决方案是使用async/await 使状态更改异步触发,以导致更改不被批处理(关于此答案https://stackoverflow.com/a/53048903/9787887

      React.useEffect(async () => {
         await setFoo(getFoo(props.fooList, props.fooId));
         await setBar(getBar(foo.listToFilter));
      }, [props.fooId]); 
      // no, actually this will not work!! it'll throw you an (annoyed) error
      
      // the actual working code is:
      React.useEffect(() =>
         const setFooAndBar = async () => {
             await setFoo(getFoo(props.fooList, props.fooId));
             await setBar(getBar(foo.listToFilter));
         }
         setFooAndBar();
      }, [props.fooId]);
      

      你看,工作代码又是另一种过于复杂(和糟糕)的解决方案,(但无论如何都应该引入??)。


      解决方案 4。

      gaearon mentioned 的另一个解决方案是使用useReducer

      • 借助 Hooks,您还可以使用 Reducer 来集中状态更新逻辑并避免这个陷阱。

      他的另一个见解:

      • 推荐的解决方案是使用一个变量而不是两个变量(因为似乎可以从另一个变量中计算出一个变量),或者首先计算下一个值并同时使用它来更新它们。或者,如果您准备好进行跳跃,useReducer 有助于避免这些陷阱。

      但这似乎又是对这个案例的另一个过于复杂的建议,不是吗?


      解决方案 5。

      最后一个建议是a comment of gaearon,告诉你重新思考你的状态依赖,状态依赖真的需要吗?

      最好的解决方案就是不要有从另一个状态计算的状态。 如果this.state.y总是从this.state.x计算,则完全删除this.state.y,只跟踪this.state.x并计算你在渲染时需要什么


      感谢您耐心阅读到这里 :))。

      【讨论】:

      • 感谢您的宝贵时间!无论如何,对我来说最简单的解决方案是第一个,即使第三个更有意义,因为 useEffect 是异步的,但代码结构过于复杂。
      • BTW 解决方案 1 不起作用,这是有道理的,因为我理解的 useEffect 是异步的
      猜你喜欢
      • 2020-04-26
      • 2019-10-17
      • 2021-05-23
      • 2019-11-26
      • 2020-01-17
      • 1970-01-01
      • 2020-06-18
      • 2021-10-21
      • 2020-06-09
      相关资源
      最近更新 更多