示例
在我的场景中,我有一个带有过滤器的侧边栏。每个过滤器都是由一个钩子(Hook)创建的:
const filters = {
customerNoFilter: useFilterForMultiCreatable(),
dateOfOrderFilter: useFilterForDate(),
requestedDevliveryDateFilter: useFilterForDate(),
deliveryCountryFilter: useFilterForCodeStable()
//.... these custom hooks are reused for like 10 more filters
}
除其他外,自定义 Hook 返回当前选定的值,
reset()
和处理程序,如 onChange
, onRemove
. (所以它不仅仅是隐藏在自定义钩子(Hook)后面的简单 useState
,请记住这一点)基本上是
reset()
函数如下所示:我还实现了一个函数来清除所有调用
reset()
的过滤器。每个过滤器的功能:const clearFilters = () => {
const filterValues = Object.values(filters);
for (const filter of filterValues) {
filter.reset();
}
};
reset()
函数在每个过滤器中触发状态更新(当然是异步的)以重置所有选定的过滤器。// setSelected is the setter comming from the return value of a useState statement
const reset = () => setSelected(initialSelected);
重置后我想用重置/更新的值和 做一些事情NOT 与状态更新前的值 ,例如使用重置的过滤器调用 API:
clearFilters();
callAPI();
在这种情况下,使用旧值调用 API(在
reset()
中更新之前)那么我怎样才能等待所有过滤器完成状态更新呢?我的代码结构很糟糕吗?我在监督什么吗?
对于单一状态更新,我可以简单地使用
useEffect
但这在等待多个状态更新时真的很麻烦..请不要认真对待这个例子,因为我经常在完全不同的情况下遇到这个问题。
最佳答案
所以我想出了一个解决方案,实现了一个名为 useStateWithPromise
的自定义钩子(Hook)。 :
import { SetStateAction, useEffect, useRef, useState } from "react";
export const useStateWithPromise = <T>(initialState: T):
[T, (stateAction: SetStateAction<T>) => Promise<T>] => {
const [state, setState] = useState(initialState);
const readyPromiseResolverRef = useRef<((currentState: T) => void) | null>(
null
);
useEffect(() => {
if (readyPromiseResolverRef.current) {
readyPromiseResolverRef.current(state);
readyPromiseResolverRef.current = null;
}
/**
* The ref dependency here is mandatory! Why?
* Because the useEffect would never be called if the new state value
* would be the same as the current one, thus the promise would never be resolved
*/
}, [readyPromiseResolverRef.current, state]);
const handleSetState = (stateAction: SetStateAction<T>) => {
setState(stateAction);
return new Promise(resolve => {
readyPromiseResolverRef.current = resolve;
}) as Promise<T>;
};
return [state, handleSetState];
};
这个钩子(Hook)将允许 await
状态更新: const [selected, setSelected] = useStateWithPromise<MyFilterType>();
// setSelected will now return a promise
const reset = () => setSelected(undefined);
const clearFilters = () => {
const promises = Object.values(filters).map(
filter => filter.reset()
);
return Promise.all(promises);
};
await clearFilters();
callAPI();
是的,我可以等待状态更新!不幸的是,如果callAPI()
,这还不是全部。依赖于更新的状态值..const [filtersToApply, setFiltersToApply] = useState(/* ... */);
//...
const callAPI = () => {
// filtersToApply will still contain old state here, although clearFilters() was "awaited"
endpoint.getItems(filtersToApply);
}
发生这种情况是因为执行了 callAPI
await clearFilters();
之后的函数is 没有重新渲染,因此它指向旧状态。但是有一个技巧需要额外的 useRef
在过滤器被清除后强制重新渲染: useEffect(() => {
if (filtersCleared) {
callAPI();
setFiltersCleared(false);
}
// eslint-disable-next-line
}, [filtersCleared]);
//...
const handleClearFiltersClick = async () => {
await orderFiltersContext.clearFilters();
setFiltersCleared(true);
};
这将确保 callAPI
在执行之前被重新渲染。就是这样!恕我直言有点乱,但它的工作原理。
如果您想了解更多有关此主题的信息,请随时查看我的 blog post .
关于reactjs - 如何在多个钩子(Hook)中等待多个状态更新?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58377204/