繁体   English   中英

如何在多个挂钩中等待多个 state 更新?

[英]How to wait for multiple state updates in multiple hooks?

例子

在我的场景中,我有一个带有过滤器的侧边栏。每个过滤器都是由一个钩子创建的:

const filters = {
  customerNoFilter: useFilterForMultiCreatable(),
  dateOfOrderFilter: useFilterForDate(),
  requestedDevliveryDateFilter: useFilterForDate(),
  deliveryCountryFilter: useFilterForCodeStable()
  //.... these custom hooks are reused for like 10 more filters 
}

除其他外,自定义钩子返回当前选定的值、 reset()和处理程序,如onChangeonRemove (所以它不仅仅是隐藏在自定义钩子后面的简单useState ,请记住这一点)

基本上reset()函数如下所示:

我还实现了一个 function 来清除所有为每个过滤器调用reset() function 的过滤器:

const clearFilters = () => {
    const filterValues = Object.values(filters);
    for (const filter of filterValues) {
      filter.reset();
    }
  };

reset() function 正在触发每个过滤器中的 state 更新(当然是异步的)以重置所有选定的过滤器。

// setSelected is the setter comming from the return value of a useState statement
const reset = () => setSelected(initialSelected);

在重置之后,我想使用重置/更新的值而不是使用 state 更新之前的值进行处理,例如使用重置的过滤器调用 API:

clearFilters();
callAPI();

在这种情况下,使用旧值调用 API(在reset()更新之前)那么我如何等待所有过滤器完成 state 更新? 我的代码结构很糟糕吗? 我在监督什么吗?

对于单个 state 更新,我可以简单地使用useEffect但这在等待多个 state 更新时真的很麻烦。

请不要认真对待这个例子,因为我经常在完全不同的情况下遇到这个问题。

所以我想出了一个解决方案,实现了一个名为useStateWithPromise的自定义钩子:

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];
};

这个钩子将允许await state 更新:

 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();

是的,我可以等待 state 更新! 不幸的是,如果callAPI()依赖于更新的 state 值,这还不是全部。

const [filtersToApply, setFiltersToApply] = useState(/* ... */);

//...

const callAPI = ()  => {
   // filtersToApply will still contain old state here, although clearFilters() was "awaited"
   endpoint.getItems(filtersToApply); 
}

这是因为在 await clearFilters await clearFilters();之后执行的callAPI function; is 没有重新渲染,因此它指向旧的 state。 但是有一个技巧需要额外的useRef在过滤器被清除后强制重新渲染:

 useEffect(() => {
    if (filtersCleared) {
      callAPI();
      setFiltersCleared(false);
    }
    // eslint-disable-next-line
  }, [filtersCleared]);

//...

const handleClearFiltersClick = async () => {
    await orderFiltersContext.clearFilters();
    setFiltersCleared(true);
};

这将确保callAPI在执行之前被重新渲染。

而已。 恕我直言有点乱,但它的工作原理。


如果您想了解更多有关此主题的信息,请随时查看我的博客文章

肯定有其他方法可以实现这一目标,但我想到了这个。 您可以拥有一个名为hasReset的 state 变量,并在 clearFilters clearFilters()之后将其设置为true 并且可以产生监听hasReset的效果,如果它是true则运行callApi()并将hasReset设置为false

例子:

const [hasReset, setHasReset] = useState(false);

useEffect(() => {
  if (hasReset) {
    callApi();
    setHasReset(false);
  }
}, [hasReset]);

const clearFilters = () => {
  const filterValues = Object.values(filters);
  for (const filter of filterValues) {
    filter.reset();
  }
  setHasReset(true);
};

要回答上述问题,如果您在自定义挂钩中使用 useState,则无需担心异步部分。 UseState 钩子只是为每个渲染声明一个变量,它与 class 组件内部的 this.setState 不同,它也会按顺序运行。

这是相关文章https的链接://overreacted.io/why-do-hooks-rely-on-call-order/

顺便说一句,我实际上也感到困惑,为什么我们需要这么多过滤器挂钩,这似乎具有相似的逻辑。 为什么不将选项传递给一个过滤器挂钩并在其中运行逻辑。 我还认为有一个管理 state 的减速器是一种更好的方法,你不希望那些钩子有自己的 state 和价值回报

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM