反应挂钩函数依赖

时间:2019-12-10 17:19:19

标签: javascript reactjs react-hooks

我发现自己处于一种奇怪的状况。我正在实现一个钩子,我无法设法实现自己想要的目标。

我有这样的东西:

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状态是一个新数组,但是取决于foo状态的setBar会收到一个空数组。基本上setBar是在setFoo完成之前执行的,因此getBar函数会收到一个空数组。

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

谢谢, F。

3 个答案:

答案 0 :(得分:3)

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

在两种情况下为什么不简单提及道具?

React.useEffect(() => {
   const newFoo = getFoo(props.fooList, props.fooId);

   setFoo(newFoo);
   setBar(getBar(newFoo.listToFilter));
}, [props.fooId]);

答案 1 :(得分:3)

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

下面,我将描述我到目前为止在整个研究过程中所学到的思想,并通过博客等方式提出人们的 5个建议/解决方案


您已经说过:

  

我的问题是setFoo函数已正确执行,因此foo状态是一个新数组,但是取决于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正在将useEffect的依赖项列表用于setBarfoo

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

React.useEffect(() => {
    setBar(getBar(foo.listToFilter));
}, [foo]);

尽管答案得到27票赞成,但我认为情况太复杂了,而且(据我所知)也应避免使该组件不必要地重新渲染2次而不是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并计算渲染时所需的内容


感谢您有足够的耐心阅读这里:))。

答案 2 :(得分:2)

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

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



 React.useEffect(() => {
    setBar(getBar(foo.listToFilter));
 }, [foo]);