Отмените все подписки в функции очистки useEffect, созданной Context.Consumer. - PullRequest
0 голосов
/ 13 июня 2019

Каждый раз, когда выполняется onClick, я получал предупреждение об утечке памяти. Как компонент может быть отписан от Context.Consumer в моем функциональном компоненте с useEffect hook?

Я не нашел способа отписаться от 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) в маршруте (создан withRouter (UserPage)) в withRouter (LoginPage) (созданный Context.Consumer) в маршруте (создан UserRoute)

Ответы [ 2 ]

0 голосов
/ 13 июня 2019

Ваша проблема в том, что 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);
0 голосов
/ 13 июня 2019

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

См. Документы , как требовать очистки послеэффект.

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

    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });
...