首页 > 解决方案 > 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 state 是一个新数组,但是依赖于 foo 状态的 setBar 接收一个空数组。基本上 setBar 在 setFoo 完成之前执行,因此 getBar 函数接收一个空数组。

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

谢谢,F。

标签: javascriptreactjsreact-hooks

解决方案


TL;博士; 您的解决方案可能kind user是答案

下面我将描述到目前为止我在整个研究过程中的想法和学到的东西,并通过博客提出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 is using useEffectto setBarwith the foo.

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提到的另一个解决方案是使用useReducer

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

他的另一个见解:

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

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


解决方案 5。

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

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


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


推荐阅读