反应钩子函数依赖关系



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

我有这样的东西:

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.

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

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


你说过:

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

你是真的。基本上是因为在 React(钩子和类组件(中,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次而不是 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.x计算this.state.y,则完全删除this.state.y,仅跟踪this.state.x并计算渲染时需要的内容


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

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

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

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

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

为什么不在这两种情况下简单地参考道具呢?

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

相关内容

  • 没有找到相关文章

最新更新