Отмените все подписки в функции очистки useEffect, созданной Context.Consumer.

Каждый раз когда 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)

3 ответа

Ваша проблема в том, что axios возвращает обещание, поэтому, когда компонент монтируется, он выполняет axios.post(...) по щелчку. Когда он ТОЛЬКО размонтируется (хотя обещание еще может быть "незавершенным"), setState его finally выполнит ПОСЛЕ того, как компонент размонтирован.

Вы можете легко проверить, смонтирован ли компонент:

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

Благодаря @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);
        }
      });
  }

Как говорится в предупреждении, в вашем UserPage компонент, который вы должны выполнить очистку useEffect чтобы избежать утечек памяти.

Обратитесь к Документам, как требовать очистки после воздействия.

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

    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });
Другие вопросы по тегам