Почему я получаю следующее предупреждение React Native: Не удается выполнить обновление состояния React для несмонтированного компонента

Я пытаюсь создать приложение React Native, но я все еще новичок в экосистеме React/RN, поэтому я могу просто неправильно понять что-то очевидное в связи с проблемой, с которой я столкнулся.

У меня есть приложение, в котором многие страницы структурированы следующим образом:

<View>
    <NavComponent />
    <View>
        {/* Page component structure/logic here */}
    </View>
</View>

NavComponent загружает переключаемое навигационное меню с TouchableOpacity элементами, как показано ниже:

Go to Screen #1
Go to Screen #2
Go to Screen #3

Проблема, с которой я столкнулся (и, возможно, это не столько проблема, сколько то, как работает React/RN), заключается в том, что если я начну с экрана №1, открою навигацию, перейду на экран №2, снова открою навигацию, а затем вернитесь к экрану № 1, хотя экран № 1 снова появляется, фактическая функция рендеринга для экрана № 1, похоже, больше не вызывается, и поскольку NavComponent является частью рендеринга каждого экрана, когда я пытаюсь чтобы снова открыть навигацию с экрана № 1, я получаю следующее предупреждение:

Предупреждение: невозможно выполнить обновление состояния React для несмонтированного компонента. Это не работает, но указывает на утечку памяти в вашем приложении. Чтобы исправить, отмените все подписки и асинхронные задачи в %s.%s, функция очистки useEffect...

Опять же, возможно, мой подход к приложению изначально ошибочен, но, по сути, когда я перехожу с одного экрана на другой из навигации, я всегда хочу, чтобы новый экран повторно отображался с нуля (включая первоначальный вызов данных Ajax) .

Вот более подробный пример функции рендеринга для экрана (все они следуют одному и тому же базовому шаблону):

const screen1 = ({ navigation }) => {
    const [serverData, setServerData] = useState(null);

    useEffect(() => {
        // getPageData is a custom method I added to axios.
        axios.getPageData('/api/url/here', (data) => {
            setServerData(data);
        });
    }, []);

    if (serverData) {
        const { meta, user, data } = serverData;

        return (
            <View>
                <NavComponent />
                <View style={styles.container}>
                    {/* Page component structure/logic here */}
                </View>
            </View>
        );
    }

    return null;
};

Если, например, я добавил console.log в начало функции рендеринга выше, она вызывается при первой загрузке экрана, но если я перейду на экран №2, а затем вернусь на экран №1 через компонент навигации, console.log больше не выводится. Почему?

И что бы это ни стоило, я использую стандартные navigation.navigate('ScreenName') в NavComponent для перехода от экрана к экрану.

Будем очень признательны за любые советы о том, как исправить предупреждение (и/или просто улучшить дизайн приложения), чтобы у меня была эта навигация на каждой странице. Спасибо.


person HartleySan    schedule 17.03.2020    source источник


Ответы (1)


ваш вызов API приводит к предупреждению в реактивной/собственной экосистеме, когда компонент удаляется из дерева, разработчику необходимо отменить все подписки (прослушиватели событий) и асинхронные задачи (извлечение данных из Интернета), эти функции должен быть отменен разработчиком, поскольку react/native не может сделать это за вас.

чтобы справиться с этим в компоненте на основе класса, вам нужно внедрить componentWillUnmount и удалить там подписки

class MyClass extends Component {
   componentWillUnmount() {
     // remove listeners and cancel requests
    }

но в современном компоненте хука вам нужно вернуть функцию очистки, функцию для возврата в useEffect, которая будет вызываться реакцией для отмены любых сделанных вами подписок, в вашем случае просто верните очистку функция должна удалить это предупреждение для вас


  const [mounted, setIsMounted] useState(false)
 useEffect(() => {
        // getPageData is a custom method I added to axios.
        setIsMounted(true)
        axios.getPageData('/api/url/here', (data) => {
            if(isMounted)
             setServerData(data);
        });

        return () => {
             setIsMounted(false)
         }
    }, []);

person Ahmed Khattab    schedule 17.03.2020
comment
Ахмед, спасибо за объяснение. Я совершенно не знал об этом. К сожалению, я добавил эту пустую функцию обратного вызова в useEffect, но я по-прежнему получаю ту же ошибку, что и раньше, и я все еще не вижу console.log, который я добавил в верхнюю часть экрана № 1, чтобы убедиться, что он каждый раз перерисовывается. . Кроме того, хотя изначально у меня его не было, я добавил хук useEffect в NavComponent и вернул пустую функцию, но все равно получаю ту же ошибку. Любые идеи? - person HartleySan; 17.03.2020
comment
как насчет экрана №2 @HartleySan - person Ahmed Khattab; 17.03.2020
comment
Да, я сделал это на обоих. Без изменений. - person HartleySan; 17.03.2020
comment
эта статья может помочь вам juliangaramendy.dev/use-promise-subscription - person Ahmed Khattab; 17.03.2020
comment
Ахмед, это интересно, но после дальнейшего изучения я вижу несколько вещей: 1) Предупреждение появляется только на экране №1, который загружается автоматически после входа в приложение. 2) Если я сменю экран, загружаемый после входа в систему, с экрана №1 на экран №2, то предупреждение появится только для экрана №2. 3) Предупреждение, по-видимому, связано с тем фактом, что все остальные экраны правильно перезагружаются каждый раз, когда я перехожу к ним, за исключением начального экрана, загружаемого при входе в систему. Мне даже не нужно возвращать () => {};, чтобы это произошло. Любые идеи? - person HartleySan; 17.03.2020
comment
возможно, на экране входа в систему вы делаете что-то, что вызывает это - person Ahmed Khattab; 17.03.2020
comment
Точнее, useEffect вызывается только при первой загрузке начальной страницы, но вызывается каждый раз для всех остальных страниц. Для экрана входа в систему я отправляю учетные данные пользователя на сервер и, получив положительный ответ, делаю следующее: navigation.navigate('Screen1');. Почему это было бы странно? - person HartleySan; 17.03.2020
comment
дозируйте предупреждение, скажите, какой экран вызвал это, пожалуйста, сделайте !! - person Ahmed Khattab; 17.03.2020
comment
Хороший вопрос, Ахмед. Что интересно, хотя проблема(ы) кажется на экране №1, ошибка указывает на то, что NavComponent является проблемой на экране №2. Ошибка очень конкретно указывает на строку <NavComponent /> на экране №2. Так запутался. Кроме того, большое спасибо за вашу помощь. - person HartleySan; 17.03.2020