useFetch无法在未安装的组件上执行React状态更新警告

时间:2019-09-13 11:15:45

标签: reactjs axios react-hooks fetch-api

我有一个自定义钩子,用于在我的react前端应用程序上发出API请求,但是该钩子似乎有一个错误。

它按预期发出API请求,但是每当我卸载正在其中发出请求的当前容器/页面时,我的钩子都不知道该页面已被卸载,因此它不会取消请求,因此会做出反应“无法在未安装的组件上执行React状态更新”警告。

export function useFetch(initialValue, url, options, key) {
  const [response, setResponse] = useLocalStorage(key, initialValue);
  const [error, setError] = useState(null);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    const CancelToken = axios.CancelToken;
    const source = CancelToken.source();
    const isMounted = { state: true };

    async function fetchData() {
      setLoading(true);
      try {
        const res = await axios({
          url: url,
          baseURL: BASE_URL,
          cancelToken: source.token,
          ...options
        });
        if (res.data.results) {
          setResponse(res.data.results);
        } else {
          setResponse(res.data);
        }
        setLoading(false);
      } catch (error) {
        setError(error);
        setLoading(false);
      }
    }
    if (isMounted.state) {
      fetchData();
    }

    return () => {
      isMounted.state = false;
      source.cancel('Operation canceled by the user.');
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [url]);
  return [response, { error, loading }];
}

2 个答案:

答案 0 :(得分:0)

现在,您在错误的位置检查if(isMounter.state)。初始化后,这是下一步。

我相信应该是

    const isMounted = { state: true };

    async function fetchData() {
      setLoading(true);
      try {
        const res = await axios({
          url: url,
          baseURL: BASE_URL,
          cancelToken: source.token,
          ...options
        });
        if(!isMounted.state) return;
        .....
      }
    }
    fetchData();

顺便说一句,您不必在那里使用对象:isMounted = true / isMounted = false可以通过关闭正常工作。

实际上,您有两种混合使用的方法:使用标志(isMounted)和取消请求。您可以只使用一个。取消请求应该可以正常工作(据我所知),但它会导致您的catch块被执行:

} catch (error) {
  setError(error);
  setLoading(false);
}

看,卸载取消请求,但是您的代码仍然尝试设置某些状态。也许您最好用axious.isCancel检查请求是否失败或取消:

} catch (error) {
  if (!axios.isCancel(error)) {
    setError(error);
    setLoading(false);
  }
}

在这种情况下,您可能会摆脱isMounted

答案 1 :(得分:0)

我使用以下钩子来获取ifMounted函数

const useIfMounted = () => {
    const isMounted = useRef(true)
    useEffect(
      () => () => {
        isMounted.current = false
      },[]
    )

    const ifMounted = useCallback(
      func => {
        if (isMounted.current && func) {
          func()
        } 
      },[]
    )

    return ifMounted
}

然后在代码中将const ifMounted = useIfMounted()添加到useFetch并在设置的函数执行ifMounted(() => setLoading(true)ifMounted(() => setError(error))等之前……

这是我写的关于该主题的博客文章:https://aceluby.github.io/blog/react-hooks-cant-set-state-on-an-unmounted-component