无法更新已卸载组件上的状态/ useEffect最后返回null

时间:2019-07-29 15:54:31

标签: reactjs firebase firebase-authentication

使用上下文和Firebase,我尝试使用onAuthStateChange在应用程序中设置我的用户。收到用户的useEffect后,将再次运行并返回null。

import React, { createContext, useState, useEffect } from 'react';
import { withFirebase } from '../Firebase';
// the context
const AuthUserContext = createContext();
// the higher order (wrapper)
const PureAuthState = ({ children, firebase }) => {
  const [authState, setAuthState] = useState({
    authUser: null,
  });
  const { auth } = firebase;
  const onChange = authUser => {
    console.log(authUser);
    setAuthState({ authUser });
  };
  useEffect(() => {
    const unsubscribe = auth.onAuthStateChanged(onChange);
    // avoid memory leaks??
    return () => {
      unsubscribe();
    };
    // eslint-disable-next-line
  }, []);
  return (
    <AuthUserContext.Provider value={{ authState, setAuthState }}>
      {children}
    </AuthUserContext.Provider>
  );
};
export const AuthState = withFirebase(PureAuthState);

export default AuthUserContext;

这些是我从浏览器的console.log中获得的内容

//// the user
context.js:12 P {G: Array(0), l: "AIzaSyBOAb8pWO...}

//// null
context.js:12 null

我不明白为什么之后会得到这个空值。正常运行2天后,发生了这种情况。救命!

编辑1: 这是我的登录组件。我认为这是这个问题的根源。

const SignInFormBase = props => {
  const [form, setVal] = useState({
    email: '',
    password: '',
    error: null,
  });
  const { firebase, history } = props;
  const { email, password, error } = form;
  const signIn = async () => {
    await firebase.doSignInWithEmailAndPassword(email, password);
    setVal({ ...form });
    history.push(ROUTES.HOME);
  };
  const onSubmit = e => {
    try {
      signIn();
      // history.push(ROUTES.HOME);
    } catch (err) {
      setVal({ ...form, error: err });
    }
    e.preventDefault();
  };

如果我将history.push到signIn函数之外,则会收到此警告,并且之后仍然仍然将authUser设置为null。

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 a useEffect cleanup function.

2 个答案:

答案 0 :(得分:0)

我不太确定您的代码设置如何,但是如果您要获取经过身份验证的用户,则可以在构造函数中执行此操作,然后调度适当的操作。

例如(ngrx 7语法):

  constructor(
    ...
  ) {
    firebase.auth().onAuthStateChanged(user => {
      // No authenticated user, logout
      if (!user) {
        return this.store.dispatch(new ActionAuthLogout());
      } 
      // User is authenticated, load user document
      else if (user) {
        return this.store.dispatch(new ActionLoadUser());
      }
    });
  }

答案 1 :(得分:0)

通过{ authState, setAuthState }时,您将在每次渲染时传递一个新引用

const PureAuthState = ({ children, firebase }) => {
  //.. rest removed for brevity.
  return (
    <AuthUserContext.Provider value={{ authState, setAuthState }}>
      {children}
    </AuthUserContext.Provider>
  );
};

在将值传递给上下文之前,您会记住它吗?

const PureAuthState = ({ children, firebase }) => {
  //.. rest removed for brevity.
  const contextValue = React.memo(() => ({ authState, setAuthState }), [
    authState,
    setAuthState
  ]);

  return (
    <AuthUserContext.Provider value={contextValue}>
      {children}
    </AuthUserContext.Provider>
  );
};