Предупреждение ReactJS «Не удается выполнить обновление состояния реакции для размонтированного компонента» при возврате Firebase promise в обработчике .then

#javascript #reactjs

#javascript #reactjs

Вопрос:

Я хочу сохранить пользователя в базе данных Firebase в реальном времени при создании пользователя в форме регистрации. Если я верну функцию Firebase (значение) для сохранения пользователей в .затем обработчик вместо того, чтобы просто вызвать его (без инструкции return), я получаю сообщение об ошибке от React, в котором говорится «Не удается выполнить обновление состояния react для размонтированного компонента».

Мой код для обработчика отправки формы регистрации выглядит примерно следующим образом:

  const SignUpFormBase = (props) => {
   const [credentials, setCredentials] = useState(INITIAL_STATE);
   const [error, setError] = useState(null);

   [some other code]

   const handleSubmit = (e) => {
        firebase
            .doCreateUserWithEmailAndPassword(email, passwordOne)
            .then(authUser => {

                // create a user in the Firebase realtime database
                return firebase.database().ref(`users/${authUser.user.uid}`)
                       .set({ username, email });

            })
            .then(() => {
                setCredentials(INITIAL_STATE);
                props.history.push(ROUTES.DASHBOARD);
            })
            .catch(error => {
                setError(error);
            });

        e.preventDefault();
    };

  return (
    [some jsx]
  );
 };

const SignUpForm = withRouter(SignUpFormBase);

export default SignUpForm;
  

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

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

Обновить:

Компонент фактически размонтируется до того, как перехватчик setCredentials получит возможность обновить состояние. Это не из-за перехода к истории в приведенном выше коде, а из-за общедоступного маршрута, который я реализовал, чтобы показывать пользователям только страницы, которые им разрешено видеть. Он использует новый перехват useContext, который запускает повторный рендеринг при изменении значения контекста (значение получено из подписки на метод firebase onAuthStateChanged). Я решил проблему, поместив вызов setCredentials в перехват useEffect:

 useEffect(() => {
    // set credentials to INITIAL_STATE before ComponentDiDUnmount Lifecycle Event
    return () => {
        setCredentials(INITIAL_STATE);
    };
}, []);
  

Однако я все еще не понимаю, почему отсутствующий оператор return (в предыдущей настройке) приводит к исчезновению предупреждения react.

Комментарии:

1. Можете ли вы поделиться setCredentials кодом?

2. Это означает, что вы уничтожаете компонент и некоторую часть кода, пытающуюся обновить состояние.

3. @UtsavPatel: обновленный блок кода.

4. Спасибо @andreW . Это решение было решением, которое я искал!

Ответ №1:

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

 firebasePromise
    .then(() => {
        // [1] async code
        setCredentials(INITIAL_STATE);

        // [2] sync code
        // UNMOUNT happens after route change
        props.history.push(ROUTES.DASHBOARD); 
    })
    .catch(error => {
        // [3] async code
        setError(error);
    });
  

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

  1. Проверьте, вызван ли [3], используйте консоль.может быть, войти?
  2. Оберните [2] в setTimeout(() => {}) , чтобы тоже сделать его асинхронным.

Моя ставка делается на [3] . каким-то образом после вызова [2] выдается какая-то ошибка.

Ответ №2:

Ваша проблема здесь:

 setCredentials(INITIAL_STATE);
props.history.push(ROUTES.DASHBOARD);
  

скорее всего, setCredentials это асинхронная функция
и когда он пытается изменить состояние, у вас уже есть другой маршрут из-за props.history.push(ROUTES.DASHBOARD)
в результате у вас нет компонента, и вы не можете обновить состояние

попробуйте:

 setCredentials(INITIAL_STATE).then(
   props.history.push(ROUTES.DASHBOARD);
)
  

Комментарии:

1. setCredential является установщиком перехвата React, он не возвращает обещание.