React Native - Предупреждение. Невозможно выполнить обновление состояния React для отключенного компонента. - PullRequest
0 голосов
/ 02 марта 2020

enter image description here enter image description here

Я пытаюсь создать простое приложение аутентификации на нативном реагировании с использованием firebase. В файле App. js я использую ловушку useEffect для инициализации экземпляра firebase в моем приложении, а также объявляю функцию для обновления локального состояния (loggedIn) всякий раз, когда пользователь входит в систему или выходит из нее. Когда я пытаюсь войти в систему с помощью электронной почты и пароля, я могу показать кнопку «Выйти», но появляется это предупреждение:

Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in %s.%s, a useEffect cleanup function, 
    in LoginForm (at App.js:25)
- node_modules\react-native\Libraries\YellowBox\YellowBox.js:63:8 in console.error
- node_modules\expo\build\environment\muteWarnings.fx.js:27:24 in error
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:645:36 in warningWithoutStack
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:20432:6 in warnAboutUpdateOnUnmountedFiberInDEV
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:18518:41 in scheduleUpdateOnFiber
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:11484:17 in dispatchAction
* [native code]:null in dispatchAction
* src\components\LoginForm.js:13:8 in LoginForm
* src\components\LoginForm.js:25:24 in onButtonPress
- node_modules\regenerator-runtime\runtime.js:45:44 in tryCatch
- node_modules\regenerator-runtime\runtime.js:271:30 in invoke
- node_modules\regenerator-runtime\runtime.js:45:44 in tryCatch
- node_modules\regenerator-runtime\runtime.js:135:28 in invoke
- node_modules\regenerator-runtime\runtime.js:145:19 in Promise.resolve.then$argument_0
- node_modules\promise\setimmediate\core.js:37:14 in tryCallOne
- node_modules\promise\setimmediate\core.js:123:25 in setImmediate$argument_0
- node_modules\react-native\Libraries\Core\Timers\JSTimers.js:146:14 in _callTimer
- node_modules\react-native\Libraries\Core\Timers\JSTimers.js:194:17 in _callImmediatesPass
- node_modules\react-native\Libraries\Core\Timers\JSTimers.js:458:30 in callImmediates
* [native code]:null in callImmediates
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:407:6 in __callImmediates
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:143:6 in __guard$argument_0
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:384:10 in __guard
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:142:17 in __guard$argument_0
* [native code]:null in flushedQueue
* [native code]:null in invokeCallbackAndReturnFlushedQueue

Что я могу сделать, чтобы это исправить?

Приложение. js (Основной входной файл):

import { StyleSheet, View } from "react-native";
import { Button } from "react-native-elements";
import { Header, Spinner, CardSection } from "./src/components/common";
import LoginForm from "./src/components/LoginForm";
import firebase from "firebase";

export default function App() {
  const [loggedIn, setLoggedIn] = useState(null);

  const renderContent = () => {
    switch (loggedIn) {
      case true:
        return (
          <CardSection>
            <View style={{ flex: 1 }}>
              <Button
                title="Log Out"
                onPress={() => firebase.auth().signOut()}
              />
            </View>
          </CardSection>
        );
      case false:
        return <LoginForm />;
      default:
        return (
          <CardSection>
            <Spinner size="large" />
          </CardSection>
        );
    }
  };

  useEffect(() => {
    if (!firebase.apps.length) {
      try {
        firebase.initializeApp({
          apiKey: "AIzaSyC6zF09VjQS9kYOK6OsiBrXeVdMWQEt-5k",
          authDomain: "auth-b4c8c.firebaseapp.com",
          databaseURL: "https://auth-b4c8c.firebaseio.com",
          projectId: "auth-b4c8c",
          storageBucket: "auth-b4c8c.appspot.com",
          messagingSenderId: "270113167666",
          appId: "1:270113167666:web:3c74e7b22f7c6cf6c6df2b",
          measurementId: "G-9EMRRJ6GKX"
        });

        firebase.auth().onAuthStateChanged(user => {
          if (user) {
            setLoggedIn(true);
          } else {
            setLoggedIn(false);
          }
        });
      } catch (err) {
        console.error("Firebase initialization error.", err.stack);
      }
    }
  }, []);
  return (
    <View>
      <Header headerText="Authentication" />
      {renderContent()}
    </View>
  );
}

LoginForm. js файл:

import React, { useState } from "react";
import { View, Text, StyleSheet } from "react-native";
import { Card, CardSection, Input, Spinner } from "./common";
import { Button } from "react-native-elements";
import firebase from "firebase";

const LoginForm = () => {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [errorMessage, setErrorMessage] = useState("");
  const [loading, setLoading] = useState(false);

  const onLoginSuccess = () => {
    setEmail("");
    setPassword("");
    setLoading(false);
    setErrorMessage("");
  };

  const onLoginFail = () => {
    setErrorMessage("Authentication failed. Try again.");
    setLoading(false);
  };

  const onButtonPress = async () => {
    setErrorMessage("");
    setLoading(true);
    try {
      await firebase.auth().signInWithEmailAndPassword(email, password);
      onLoginSuccess();
    } catch (e1) {
      console.log(e1);
      try {
        await firebase.auth().createUserWithEmailAndPassword(email, password);
        onLoginSuccess();
      } catch (e2) {
        console.log(e2);
        onLoginFail();
      }
    }
  };

  return (
    <Card>
      <CardSection>
        <Input
          secureTextEntry={false}
          placeholder="abc@example.com"
          label="Email:"
          value={email}
          onChangeText={text => setEmail(text)}
        />
      </CardSection>
      <CardSection>
        <Input
          secureTextEntry={true}
          placeholder="password"
          value={password}
          onChangeText={password => setPassword(password)}
          label="Password:"
        />
      </CardSection>

      {errorMessage ? (
        <Text style={styles.errorTextStyle}>{errorMessage}</Text>
      ) : null}

      <CardSection>
        {loading ? (
          <Spinner size="small" />
        ) : (
          <View style={{ flex: 1 }}>
            <Button title="Log in" onPress={() => onButtonPress()} />
          </View>
        )}
      </CardSection>
    </Card>
  );
};

const styles = StyleSheet.create({
  errorTextStyle: {
    fontSize: 20,
    alignSelf: "center",
    color: "red"
  }
});

export default LoginForm;

Все остальные компоненты, такие как Header / Spinner et c. не имеют никакого отношения к состоянию напрямую и предназначены для презентаций / стилей, поэтому здесь я не включаю их код.

Ответы [ 2 ]

1 голос
/ 02 марта 2020

Проблема в том, что из-за вашего переключателя в функции renderContent в какой-то момент вы больше не визуализируете LoginForm. Таким образом, он отключен, но в это время выполняется обновление состояния и выдается ошибка.

При просмотре вашего кода, вероятно, проблема возникает в LoginForm при вызове await firebase.auth().signInWithEmailAndPassword(email, password);. Фактически, после завершения входа в систему firebase.auth().onAuthStateChanged(); запускается и LoginForm отключается, но вызывается onLoginSuccess и обновляется состояние LoginForm.

Попробуйте удалить onLoginSuccess, форму следует сбросить после ее завершения. снова отображается после того, как вы размонтируете его.

0 голосов
/ 02 марта 2020

Как указано в предупреждении, вы должны предоставить функцию очистки и убедиться, что обновление состояния происходит соответствующим образом.

Обновите ваш код, как показано ниже, и проверьте, работает ли он. Я надеюсь, что это поможет вам.

let isMounted = false;
useEffect(()=> {
  if(!isMounted) {
    /* your authentication and state update code */
    isMounted = true;
  }
  return () => {
    isMounted = false;
  }
}, [isMounted]);

См. Комментарий ниже для получения более подробной информации:

https://github.com/material-components/material-components-web-react/issues/434#issuecomment -449561024

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...