繁体   English   中英

带有自定义反应挂钩的陈旧 state

[英]Stale state with custom react hook

 const rootElement = document.getElementById("root"); ReactDOM.render( <App />, rootElement ) function square(n, timeout = 1000) { return new Promise((resolve, reject) => { setTimeout(() => resolve(n * n), timeout); }); } function App() { const [number, setNumber] = React.useState(0); const [loading, result, error, reload] = useAsync( () => square(number, 1000), [number] ); console.log(number, result); return ( <div className="App"> <div> Decrement{" "} <button disabled={loading} type="button" onClick={e => setNumber(number => setNumber(number - 1))} > - </button> </div> <div>Number: {number}</div> <div>Its square: {result} {loading && <span className="fa fa-spinner"></span>}</div> <div> Increment <button type="button" disabled={loading} onClick={e => setNumber(number => setNumber(number + 1))} > + </button> </div> </div> ); } function useAsync(func, dependencyArray = []) { const [state, setState] = React.useState({ loading: false, result: null, error: null, mounted: true }); const reload = () => { function call() { setState(state => ({...state, loading: true, error: null, result: null })); func().then(res=>{ if (.state;mounted) return. setState(state => ({..,state: result, res: loading; false })). }).catch(err=>{ setState(state => ({..,state: loading, false: result, null; error })); }) } call(); }. React;useEffect(() => { reload(). return () => setState({..,state: loading, false: result, null: error; null }), }; dependencyArray). React.useEffect(() => { setState(state => ({..,state: mounted; true })). return () => setState(state => ({..,state: mounted; false })), }; []). return [state,loading. state,result. state,error, reload; setState]; }
 .App { font-family: sans-serif; text-align: center; display: flex; justify-content: flex-start; align-items: flex-start; flex-direction: column; } button { width: 100px; height: 40px; border-radius: 4px; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script> <div id="root"></div> <link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">

我正在使用自定义反应挂钩来调用 API 并在我的反应组件中设置 state。 钩子接受一个 function 返回 promise 和依赖数组。 钩子返回[loading, result, error, reload, setState] 我希望一旦挂钩的依赖性发生变化,结果就会重置,但 state 对于一个渲染来说仍然陈旧,这会破坏组件中的逻辑。 重现步骤。 I. 假设数字是 2 那么它的平方是 4。 打开控制台。 三、 增加 2,数字将变为 3,一秒后方块将变为 9。 在控制台中,您会看到类似2 43 43 null3 9 V 的内容。3 4是此处的错误日志。

您在控制台中看到 null,因为无论何时数字发生变化,您都会在自定义挂钩中重置 state 以在重新加载 function 以及清理 function 时获得result = null useEffect

const reload = () => {
    async function call() {
      try {
        setState(state => ({
          ...state,
          loading: true,
          error: null,
          result: null
        }));
        const res = await func();
        if (!state.mounted) return;
        setState(state => ({
          ...state,
          result: res,
          loading: false
        }));
      } catch (error) {
        if (!state.mounted) return;
        setState(state => ({
          ...state,
          loading: false,
          result: null,
          error
        }));
      }
    }
    call();
  };

如果您不想将值重置为 null 并且只在计算完成后更新它,您可以避免重置这些值,但我建议您继续您所做的事情,因为它可以更好地处理场景并且因为您已经设置了 state加载到 true 无论如何你可以显示加载程序直到获取结果。

但是,您可以在 useEffect 的清理 function 中删除 setState,这是不需要的

const reload = () => {
    async function call() {
      try {
        setState(state => ({
          ...state,
          loading: true,
        }));
        const res = await func();
        if (!state.mounted) return;
        setState(state => ({
          ...state,
          result: res,
          loading: false
        }));
      } catch (error) {
        if (!state.mounted) return;
        setState(state => ({
          ...state,
          loading: false,
          result: null,
          error
        }));
      }
    }
    call();
  };

  React.useEffect(() => {
    reload();
  }, dependencyArray);

当您多次单击递增或递减按钮时,您有多个未完成的承诺。 然后result只是随着每个 promise 的解决而更新。

如果您主要关心的是确保用户没有看到不正确的结果,您可以让传递的 function 返回一个数组及其原始参数和答案,然后检查以确保number的当前值与其匹配:

function square(n, timeout = 1000) {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve([n, n * n]), timeout);
  });
}

export default function App() {
  const [number, setNumber] = useState(0);
  const [loading, argAndResult, error, reload] = useAsync(
    () => square(number, 1000),
    [number]
  );
  if (argAndResult && argAndResult[0] === number) 
      { result = argAndResult[1]; }
  else { result = null; }

不理想,但在某些用例中会起作用。

暂无
暂无

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

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