How to implement Firebase authentication with Reac

2019-07-09 11:01发布

问题:

I am using Firebase to handle my authentication through Facebook or google. The thing I really do not understand is the reason why is my store not updated.

Below is an example of my code:

import { createStore } from 'redux';
import {app, facebookProvider, googleProvider } from './../config/config';

const initialState = {
    SCREEN_CURRENT: "login",
    authenticated: false
}

const reducer = (state = initialState, action) => {
console.log('reducer', action);

switch(action.type){
    case "AUTH_LOGIN_FACEBOOK":
        state = {
            ...state,
            authenticated: true
        }

        app.auth().signInWithPopup(facebookProvider)
        .then((user, error) => {
            if(error){
                console.log("Unable to login with Facebook!");
            } else {
                console.log("Logged in succesfully");
                state = Object.assign({}, state, {
                    authenticated: true
                });
            }
        }).catch((error) => {
            if(error){
                console.log("Error from Facebook: " + error.message);
            }
        });
        break;

    case "AUTH_LOGIN_GOOGLE":
        app.auth().signInWithPopup(googleProvider)
        .then((user, error) => {
            if(error){
                console.log("Unable to login with Google!");
            } else {
                console.log("Logged in succesfully");
                return Object.assign({}, state, {
                    authenticated: true
                });
            }
        }).catch((error) => {
            if(error){
                console.log("Error from Google: " + error.message);
            }
        });
        break;

    default:
        break;
}

return state;
}

const store = createStore(reducer);

store.subscribe(() => {
    console.log("Store updated", store.getState());
});

export default store;

Can someone explain me why my store is not updated, even though I change my authentication state to true on succesful login (which happened)?

I cannot understand why.

When I click the button that triggers the "AUTH_LOGIN_FACEBOOK" action, the store gets updated. However, not when I change the state of authenticated to true. How do I get the store to update?

回答1:

There's too much logic inside the reducer. First thing to do here is to move them to an action, or a plain old javascript object, or even a function. Please notice that the code above doesn't take import/export into account, but I think you'll get my point here. Therefore, you have to adapt it to your use case.

// ##########################################################################
// AUTH FUNCTIONS ###########################################################
// ##########################################################################

const FACEBOOK = 'facebook';
const GOOGLE = 'google';

// just to make sure that you'll always return a valid prodiver.
function getProviderObject(provider) {
  return {
    [FACEBOOK]: new firebase.auth.FacebookAuthProvider(),
    [GOOGLE]: new firebase.auth.GoogleAuthProvider()
  }[provider] || null;
}

// this function receives a provider object and return a User, no matter what
// provider, Google or Facebook
function signInWithProvider(provider) {  
  const providerObject = getProviderObject(provider);

  if(!providerObject) {
    throw new Error('Invalid provider');
  }

  try {
    const response = await FirebaseApp.auth().signInWithPopup(provider);

    return response.user;
  } catch(error) {
    // handle error...
    throw error;
  }
}

function signInWithFirebase({ email, password }) {
  return FirebaseApp.auth().signInAndRetrieveDataWithEmailAndPassword(email, password);
}


// ##########################################################################
// ACTIONS ##################################################################
// ##########################################################################

// this action acts like a router, which will decide to sign in with an 
// external provider (GOOGLE or FACEBOOK) or sign in with email/password
const signIn = param => async dispatch => {
  try {
    const user = await (
      [GOOGLE, FACEBOOK].includes(param) ?
        signInWithProvider(param) :
        signInWithFirebase(param)
    );

    dispatch({
      type: 'CREATE_SESSION',
      payload: user
    });

    return user;
  } catch(error) {
    console.log(error);
  }
};

// ##########################################################################
// REDUCERS #################################################################
// ##########################################################################

const initialState = { 
  authenticated: false, 
  user: null,
  // etc...
};

function sessionReducer(state = initialState, action = {}) {
  switch(action.type) {
    case 'CREATE_SESSION': return { 
      ...action.payload, 
      authenticated: true,
      // whatever props you need here...
    };

    case 'DESTROY_SESSION': return initialState;

    default: return state;
  }
}

// ##########################################################################
// EXAMPLE ##################################################################
// ##########################################################################

class Auth extends React.Component {
  state = {
    email: null,
    password: null
  };

  render() {
    const { email, password } = this.state;

    return (
      <p><a href="#" onClick={this.signInWith(FACEBOOK))>SIGN IN WITH FACEBOOK</a></p>
      <p><a href="#" onClick={this.signInWith(GOOGLE))>SIGN IN WITH GOOGLE</a></p>

      <form onSubmit={this.onSubmit}>
        <input 
          type="email" 
          onChange={this.onChange('email')} 
          value={email} placeholder="Email" 
        />

        <input 
          type="password" 
          onChange={this.onChange('email')} 
          value={password} 
          placeholder="Password" 
        />

        <button>Sign In</button>
      </form>
    )
  }

  signInWith = provider => event => {
    event.preventDefault();
    this.props.signIn(provider);
  }

  onChange = field => value => this.setState({ [field]: value });

  onSubmit = () => {
    const { email, password } = this.state;
    return this.props.signIn({ email, password });
  };
}

export default connect(null, { signIn })(Auth);

I hope this helps!