Search code examples
reactjsfirebaseauthenticationreduxredux-thunk

Firebase, react and redux wait for store to update


I have a React app connected to Firebase, i want to check if user is logged in at every page refresh, dispatching IS_SIGNED_IN action, here's the action creator:

export function checkAuth() {
return dispatch => {
    firebaseAuth().onAuthStateChanged((user) => {
        if (user) {
            dispatch(signedInAction())
        } else {
            dispatch(signedOutAction())
        }
    })
  }
}

Inside the reducer i just make boolean isAuthenticated to true/false:

case ActionTypes.isSignedIn: {
    return Object.assign({}, state, {
        isAuthenticated: true
    })
}
case ActionTypes.isSignedOut: {
    return Object.assign({}, state, {
        isAuthenticated: false
    })
}

Because i want to check if user is signed in when app loads i dispatch the action at componentDidMount (which is wrong i guess):

class Main extends Component {

constructor(props) {
    super(props);
}

componentDidMount() {
    store.dispatch(checkAuth());
}

render() {
    return (
        <Provider store={store}>
            <Router>
                <Theme>
                    <Routes />
                </Theme>
            </Router>
        </Provider>
    );
  }
}

This makes my code where i check for isAuthenticated fail, because it's not yet set in the store, this is in Routes component for example. What would be the best way to solve this? To add up, i know i can use conditional rendering and check to see when isAuthenticated is defined, but i don't like that solution. Thoughts? Thanks


Solution

  • I have done what you are trying to do with a live application and used in it Firebase Auth. You need to use the Actions as a login and logout only then use componentWillMount() and componentWillReceiveProps() to check if the user is logged in:

    Actions:

    import { auth } from '../fire';
    export const GET_USER = 'get_user';
    
    export function getUser(){
        return dispatch => {
            auth.onAuthStateChanged(user=>{
                dispatch({
                    type: GET_USER,
                    payload: user
                });
            });
        };
    }
    
    export function login(email,password){
        return dispatch => auth.signInWithEmailAndPassword(email, password);
    }
    
    export function logout(){
        return dispatch => auth.signOut();
    }
    
    export function createAccount(email, password){
        return dispatch => auth.createUserWithEmailAndPassword(email, password);
    }
    

    your Reducer should have this:

    import {GET_USER} from '../Actions/UserActions';
    
    export default function( state = {loading:true}, action){
        switch (action.type){
            case GET_USER:
                return { loading: false, ...action.payload };
            default:
            return state;
        }
    }
    

    in your App.js for example, just in the start of it use this:

     componentWillMount(){
       this.props.getUser();
       if(this.props.user.loading === false && this.props.user.email === undefined){
         this.props.history.replace('/Login');
       }
     }
    
     componentWillReceiveProps(nextProps){
      if(nextProps.user.loading === false && nextProps.user.email === undefined){
        this.props.history.replace('/Login');
      }
     }
    

    this is because you have your Auth credentials in your props already.

    I hope this works for you..