带有多个参数的useApi挂钩

时间:2019-10-24 10:22:27

标签: reactjs react-hooks

我有一个useApi自定义钩子,该钩子带有带有多个参数的端点(url)。 当参数之一更改时,将呈现一个图形。 问题在于,当一个参数更改时,另一个参数也会更改,并且图形将呈现两次。我该如何解决? 谢谢

  const useApi = (endpoint, requestType, body) => {
    const [data, setData] = useState({ fetchedData: [], isError: false, isFetchingData: false });
    useEffect(() => {
        requestApi();
    }, [endpoint]);
    const requestApi = async () => {
        let response = {};
        try {
            setData({ ...data, isFetchingData: true });
            console.log(endpoint);
            switch (requestType) {
                case 'GET':
                    return (response = await axios.get(endpoint));
                case 'POST':
                    return (response = await axios.post(endpoint, body));
                case 'DELETE':
                    return (response = await axios.delete(endpoint));
                case 'UPDATE':
                    return (response = await axios.put(endpoint, body));
                case 'PATCH':
                    return (response = await axios.patch(endpoint, body));
                default:
                    return (response = await axios.get(endpoint));
            }
        } catch (e) {
            console.error(e);
            setData({ ...data, isError: true });
        } finally {
            if (response.data) {
                setData({ ...data, isFetchingData: false, fetchedData: response.data.mainData });

            }
        }
    };
    return data;
};

1 个答案:

答案 0 :(得分:1)

有几个地方可以重构:

首先,您可以将data中的useEffect依赖项转换为如下形式:

setData(currentData => {
  return { ...currentData, isFetchingData: true }
})

第二个也是最重要的一点是,您应该将requestApi函数移到useEffect的内部,或者用useCallback函数包装它。

最后,如果后面有多个渲染,完全可以。因为您依赖于useEffect内部的所有参数。

您可以做的一件事是,利用returning a function in useEffect在卸载期间取消axios请求

这是代码的最终版本:

const useApi = (endpoint, requestType, body) => {
  const [data, setData] = useState({
    fetchedData: [],
    isError: false,
    isFetchingData: false
  })
  useEffect(() => {
    let axiosSource = axios.CancelToken.source() // generate a source for axios
    let didCancel = false // we can rely on this variable.
    const requestApi = async () => {
      let response = {}
      try {
        setData(data => {
          return { ...data, isFetchingData: true }
        })
        console.log(endpoint)
        const axiosOptions = { cancelToken: axiosSource.token }
        switch (requestType) {
          case 'GET':
            return (response = await axios.get(endpoint, axiosOptions))
          case 'POST':
            return (response = await axios.post(endpoint, body, axiosOptions))
          case 'DELETE':
            return (response = await axios.delete(endpoint, axiosOptions))
          case 'UPDATE':
            return (response = await axios.put(endpoint, body, axiosOptions))
          case 'PATCH':
            return (response = await axios.patch(endpoint, body, axiosOptions))
          default:
            return (response = await axios.get(endpoint, axiosOptions))
        }
      } catch (e) {
        console.error(e)
        if (!didCancel) {
          setData(data => {
            return { ...data, isError: true }
          })
        }
      } finally {
        // do not update the data if the request is cancelled
        if (response.data && !didCancel) {
          setData(data => {
            return {
              ...data,
              isFetchingData: false,
              fetchedData: response.data.mainData
            }
          })
        }
      }
    }
    requestApi()
    // Here we are saying to axios cancel all current ongoing requests
    // since this is the cleanup time.
    return () => {
      didCancel = true
      axiosSource.cancel()
    }
  }, [body, endpoint, requestType])
  return data
}

我没有测试代码。但它应该起作用。请尝试告诉我发生了什么。