reactjs - 如何在多个钩子(Hook)中等待多个状态更新?

标签 reactjs react-hooks

示例

在我的场景中,我有一个带有过滤器的侧边栏。每个过滤器都是由一个钩子(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/

相关文章:

javascript - ReactJS 无状态组件参数

reactjs - 使用具有日期时间本地类型的material-ui TextField 时是否会阻止输入清除?

javascript - React 功能组件 - 如何计算实例?

javascript - React 动态表单输入 useRef typeError

javascript - react / react Hook : Need to run a function onLoad within React Hooks component

javascript - 如何获取样式中的 react 动态背景图像值?

javascript - 在 React.JS 中,如何在渲染字段的组件上设置 readOnly 属性

javascript - 将 Webpack 与 React-router bundle.js 一起使用未找到

javascript - 谷歌浏览器不尊重缓存控制中的最大年龄

javascript - React Router 的 HashRouter 重定向到 <base> 标签 url