我如何在usePromise React钩子中将参数传递给Promise

时间:2019-10-28 18:44:02

标签: javascript reactjs promise react-hooks react-16

我创建了一个usePromise React Hook,它应该能够解析各种JavaScript承诺并返回每种结果和状态:数据,解析状态和错误。 我可以通过没有任何参数的函数来使它正常工作,但是当我尝试更改它以允许参数时,就会出现无限循环。

const usePromise = (promise: any): [any, boolean, any] => {
  const [data, setData] = useState<object | null>(null);
  const [error, setError] = useState<object | null>(null);
  const [fetching, setFetchingState] = useState<boolean>(true);

  useEffect(() => {
    setFetchingState(true);
    promise
      .then((data: object) => {
        setData(data);
      })
      .catch((error: object) => {
        setError(error);
      })
      .finally(() => {
        setFetchingState(false);
      });
  }, [promise]);

  return [data, fetching, error];
};

const apiCall = (param?: string): Promise<any> => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({ response: `Response generated with your param ${param}.` });
    }, 500);
  });
};

const App = (): React.Element => {
  // How can I pass an argument to apiCall?
  const [response, fetching, error] = usePromise(apiCall(5));
  console.log("render"); // This logs infinitely
  return <div>{JSON.stringify({ response, fetching, error })}</div>;
};

您可以在以下位置查看工作代码(无参数):https://codesandbox.io/s/react-typescript-fl13w

bug ,位于(该标签​​卡住了,建议):https://codesandbox.io/s/react-typescript-9ow82

注意:我想找到解决方案而不使用NPM或类似的usePromise单功能库

1 个答案:

答案 0 :(得分:1)

自定义钩子可能会执行多次。您应该以这种方式进行设计,使您只想做一次的所有事情(例如API调用)都位于useEffect挂钩中。这可以通过执行一个回调然后在一个钩子中调用来实现。

此外,类型安全性还稍微高一点:

 const usePromise = <T>(task: () => Promise<T>) => {
   const [state, setState] = useState<[T?, boolean, Error?]>([null, true, null]);


   useEffect(() => {
       task()
         .then(result => setState([result, false, null])
         .catch(error => setState([null, false, error]);      
  }, []); // << omit the condition here, functions don't equal each other²

  return state;
};

// Then used as 
usePromise(() => apiCall(5));

²是的,通常这是一种不好的做法,但是由于task不应在此处更改,我认为这很好


根据要求,这是我在某些项目中使用的版本:

 export function useAPI<Q, R>(api: (query: Q) => Promise<R | never>) {
  const [state, setState] = useState<{ loading?: true, pending?: true, error?: string, errorCode?: number, result?: R }>({ pending: true });

  async function run(query: Q) {
    if(state.loading) return;
    setState({ loading: true });

    try {
        const result = await api(query);
        setState({ result });
    } catch(error) {
        if(error instanceof HTTPError) {
            console.error(`API Error: ${error.path}`, error);
            setState({ error: error.message, errorCode: error.code });
        } else {
            setState({ error: error.message, errorCode: NaN });
        }
    }
  }

  function reset() {
     setState({ pending: true });
  }

  return [state, run, reset] as const;

}