useFetch Can't perform a React state update on an unmounted component 警告

Posted

技术标签:

【中文标题】useFetch Can\'t perform a React state update on an unmounted component 警告【英文标题】:useFetch Can't perform a React state update on an unmounted component warninguseFetch Can't perform a React state update on an unmounted component 警告 【发布时间】:2020-01-15 06:29:30 【问题描述】:

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

它按预期发出 API 请求,但每当我卸载发出请求的当前容器/页面时,我的钩子不知道该页面已被卸载,因此它不会取消请求并因此反应抛出'Can't perform a React state update on an unmounted component' 警告。

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 ];


【问题讨论】:

【参考方案1】:

现在您在错误的位置检查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

【讨论】:

【参考方案2】:

我使用下面的钩子来获取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

【讨论】:

以上是关于useFetch Can't perform a React state update on an unmounted component 警告的主要内容,如果未能解决你的问题,请参考以下文章

如何解构来自 useFetch 的对象?

react实战笔记132:完成useFetch3创建

从服务器获取数据并将值传递给 React 中的子组件(useEffect、useFetch..)

Can't migrate a table to Room do to an error with the way booleans is saved in Sqlite

如何处理来自 urllib.request.urlopen() 的响应编码,以避免 TypeError: can't use a string pattern on a bytes-like obje

Python 3.8 TypeError: can't concat str to bytes - TypeError: a bytes-like object is required, not 's