【问题标题】:Cancel all subscriptions in a useEffect cleanup function created by Context.Consumer在 Context.Consumer 创建的 useEffect 清理函数中取消所有订阅
【发布时间】:2019-10-28 06:15:54
【问题描述】:

每次onClick 执行时,我都会收到有关内存泄漏的警告消息。如何使用 useEffect 钩子从我的功能组件中的 Context.Consumer 取消订阅组件?

我没有找到取消订阅 AppContext 的方法。 AppContext.unsubsribe() 没用。

import React, {useState, useContext} from 'react';
import {withRouter} from 'react-router-dom';
import axios from 'axios';
import {AppContext} from "../context/AppContext";

const LoginPage = (props) => {

    const [name, setName] = useContext(AppContext);
    const [isLoading, setIsLoading] = useState(false);

    const onClick = () => {
        setIsLoading(true);
        axios.post('/get-name')
            .then(resp => {
                setName(resp);
                setIsLoading(false);
                props.history.push('/');
            })
            .catch(err => console.log(err))
            .finally(() => setIsLoading(false));
    };

    return (
        <div>
            <button onClick={onClick}></button>
        </div>
    );
};

export default withRouter(LoginPage);

浏览器控制台中的错误消息:

警告:无法对未安装的对象执行 React 状态更新
零件。这是一个无操作,但它表明您的内存泄漏 应用。要修复,请取消所有订阅和异步任务 在 useEffect 清理函数中。 在 UserPage 中(由 Context.Consumer 创建) 在 Route 中(由 withRouter(UserPage) 创建) 在 withRouter(LoginPage) 中(由 Context.Consumer 创建) 在Route中(由UserRoute创建)

【问题讨论】:

  • 问题真的出在 AppContext 上吗?我认为问题在于您在卸载组件后正在执行setIsLoading
  • 我应该如何对setIsLoading 方法和isLoading 属性执行清理?

标签: reactjs react-hooks react-context use-effect


【解决方案1】:

你的问题是 axios 正在返回一个承诺,所以当组件被挂载时,它会在点击时执行axios.post(...)。当它然后卸载时(而承诺仍然可能“未完成”),其 finallysetState 将在组件卸载后执行。

您可以使用简单的检查组件是否已安装:

import React, {useState, useContext, useEffect} from 'react';
import {withRouter} from 'react-router-dom';
import axios from 'axios';
import {AppContext} from "../context/AppContext";

const LoginPage = (props) => {

    const [name, setName] = useContext(AppContext);
    const [isLoading, setIsLoading] = useState(false);
    const isMounted = useRef(null);

    useEffect(() => {
      // executed when component mounted
      isMounted.current = true;
      return () => {
        // executed when unmount
        isMounted.current = false;
      }
    }, []);

    const onClick = () => {
        setIsLoading(true);
        axios.post('/get-name')
            .then(resp => {
                setName(resp);
                setIsLoading(false);
                props.history.push('/');
            })
            .catch(err => console.log(err))
            .finally(() => {
               if (isMounted.current) {
                 setIsLoading(false)
               }
            });
    };

    return (
        <div>
            <button onClick={onClick}></button>
        </div>
    );
};

export default withRouter(LoginPage);

【讨论】:

    【解决方案2】:

    如警告所述,在您的 UserPage 组件中,您需要对 useEffect 执行清理以避免内存泄漏。

    参考Docs如何在效果后要求清理。

      useEffect(() => {
        function handleStatusChange(status) {
          setIsOnline(status.isOnline);
        }
    
        ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
        return () => {
          ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
        };
      });
    

    【讨论】:

    • 我在文档中看到了这个例子。对我来说,不清楚在我的情况下应该执行哪些方法。我没有订阅可以提供unsubscribe 方法的外部组件,例如ChatAPI.unsubscribeFromFriendStatus(...)
    • 添加你的UserPage,最好只做一个沙箱codesandbox.io/s/new
    【解决方案3】:

    感谢@Bennet Dams,我可以解决我的问题,这是我按照他的示例编写的代码

      const isMounted = useRef(null);
    
      useEffect(() => {
        isMounted.current = true;
        fetchRequestsData();
        return () => {
          isMounted.current = false;
        };
      }, []);
    
      async function fetchRequestsData() {
      
      //My previous code which led to the warning
        /* const { data } = await axios({
          url: '../api/random/my-requests',
          method: 'get',
        });
        setSuspendedRequests(data.suspended); */
    
        let data;
        axios
          .get('../api/random/my-requests')
          .then((resp) => {
            data = resp.data;
          })
          .catch((err) => console.log(err))
          .finally(() => {
            if (isMounted.current) {
              setSuspendedRequests(data.suspended);
            }
          });
      }
    

    【讨论】:

      猜你喜欢
      • 2021-01-28
      • 2021-03-24
      • 2019-10-20
      • 2021-06-23
      • 2020-03-31
      • 1970-01-01
      • 2020-12-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多