Promise 函数延迟状态变量

Promise function delays state variable

您如何正确地从 promise 函数存储值而不延迟?

我正在尝试使用 useEffect 挂钩,但我的状态仍然延迟。

这是有问题的,因为如果用户正在验证他的购物车,可能会应用错误的税。

useEffect(() => {
SalesTax.getSalesTax(country, region).then((tax) => {
      setTax(tax.rate);
    });
}, [region]);
      

<RegionDropdown
            country={country}
            className="capitalize input"
            value={region}
            onChange={(val) => {
              setRegion(val);
            }}
            required
            countryValueType="short"
            valueType="short"
          />

你应该这样做:

const [loading, setLoading] = useState(false);

useEffect(() => {
    setLoading(true);
    SalesTax.getSalesTax(country, region).then((tax) => {
        setTax(tax.rate);
        setLoading(false);
    });
}, [region]);

if (loading) {
    return <p>Loading...<p/>;
}
      

这是在 React 中处理异步操作的正常方式。

您将使用大量异步值,那么制作一个钩子(例如 useAsync 怎么样?

function useAsync(f, [_0, _1, _2, _3, _4]) {
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(null)
  const [result, setResult] = useState(null)
  useEffect(_ => {
    setLoading(true)
    Promise.resolve(f(_0, _1, _2, _3, _4))
      .then(setResult, setError)
      .finally(_ => setLoading(false))
  }, [f, _0, _1, _2, _3, _4])
  return {loading, error, result}
}

现在在你的组件中使用你的钩子 -

function MyComponent({country, region}) {
  const {loading, error, result} =
    useAsync(SalesTax.getSalesTax, [country, region])
  if (loading)
    return <p>Loading...</p>
  else if (error)
    return <p>Error: {error.message}</p>
  else
    /* result is usable here */
    return <RegionDropdown .../>
}

_0, _1, _2, ... 是必需的,因此 ESLint 可以静态分析依赖关系。在未来的版本中,也许可以使用数组。另一种选择是为此挂钩禁用 ESLint,但请注意这是 discouraged by Dan Abramov of React -

function useAsync(f, args) {
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(null)
  const [result, setResult] = useState(null)
  useEffect(_ => {
    setLoading(true)
    Promise.resolve(f(...args))
      .then(setResult, setError)
      .finally(_ => setLoading(false))
  },
  // eslint-disable-next-line react-hooks/exhaustive-deps
  [f, ...args])
  return {loading, error, result}
}