6

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

我没有找到如何取消订阅 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 创建)

4

3 回答 3

7

您的问题是 axios 正在返回一个承诺,因此当组件安装时,它会axios.post(...)在单击时执行。当它然后卸载时(而承诺仍然可能“未完成”),setStatefinally将在组件卸载后执行。

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

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);
于 2019-06-13T18:52:21.407 回答
0

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

请参阅文档如何在效果后要求清理。

  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });
于 2019-06-13T16:03:55.753 回答
0

感谢@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);
        }
      });
  }
于 2022-02-24T09:14:22.510 回答