Firebase React Context setup

React context

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import Firebase, { FirebaseContext } from './Components/Firebase';

ReactDOM.render(
  <FirebaseContext.Provider value={ new Firebase() }>
    <App />
  </FirebaseContext.Provider>,
  document.getElementById('root')
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA
serviceWorker.unregister();

https://github.com/bbc/digital-paper-edit-client/blob/firebase/src/Components/Firebase/context.js

Firebase class

import app from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';

const config = {
  apiKey: process.env.REACT_APP_API_KEY,
  authDomain: process.env.REACT_APP_AUTH_DOMAIN,
  databaseURL: process.env.REACT_APP_DATABASE_URL,
  projectId: process.env.REACT_APP_PROJECT_ID,
  storageBucket: process.env.REACT_APP_STORAGE_BUCKET,
  messagingSenderId: process.env.REACT_APP_MESSAGING_SENDER_ID
};
class Firebase {
  constructor() {
    app.initializeApp(config);
    this.auth = app.auth();
    this.db = app.firestore();
  }

  userRef = uid => this.db.doc(`users/${ uid }`);
  usersRef = () => this.db.collection('users');

  user = async uid => await this.userRef(uid).get();
  users = async () => await this.usersRef().get();
  // *** Merge Auth and DB User API *** //

  onAuthUserListener = (next, fallback) =>
    this.auth.onAuthStateChanged(async authUser => {
      if (authUser) {
        const dbSnapshot = await this.user(authUser.uid);
        const dbUser = dbSnapshot.data();

        // default empty roles
        if (!dbUser.roles) {
          dbUser.roles = {};
        }

        // merge auth and db user
        const mergeUser = {
          uid: authUser.uid,
          email: authUser.email,
          emailVerified: authUser.emailVerified,
          providerData: authUser.providerData,
          ...dbUser
        };

        next(mergeUser);
      } else {
        fallback();
      }
    });

  // doCreateUserWithEmailAndPassword = (email, password) =>
  // this.auth.createUserWithEmailAndPassword(email, password);
  doSignInWithEmailAndPassword = (email, password) =>
    this.auth.signInWithEmailAndPassword(email, password);
  doSignOut = () => this.auth.signOut();
  doPasswordReset = email => this.auth.sendPasswordResetEmail(email);
  doPasswordUpdate = password => this.auth.currentUser.updatePassword(password);
}

export default Firebase;

d

import FirebaseContext, { withFirebase } from './context';
import Firebase from './firebase';

export default Firebase;

export { FirebaseContext, withFirebase };

Higher order component - consumer

import React from 'react';

const FirebaseContext = React.createContext(null);

export const withFirebase = Component => props => (
  <FirebaseContext.Consumer>
    {firebase => <Component { ...props } firebase={ firebase } />}
  </FirebaseContext.Consumer>
);
export default FirebaseContext;

Example usage

eg in a sign in page

https://github.com/bbc/digital-paper-edit-client/blob/firebase/src/Components/SignIn/index.js

import React, { useState } from 'react';
import { withRouter } from 'react-router-dom';
import { compose } from 'recompose';
import { withFirebase } from '../Firebase';
import * as ROUTES from '../../constants/routes';
const SignInPage = () => (
  <div>
    <h1>SignIn</h1>
    <SignInForm />
  </div>
);

const SignInFormBase = props => {
  const [ email, setEmail ] = useState('');
  const [ password, setPassword ] = useState('');
  const [ error, setError ] = useState();

  // TODO: IF signed in, forward always to somewhere else
  const onSubmit = async event => {
    try {
      await props.firebase.doSignInWithEmailAndPassword(email, password);
      setEmail('');
      setPassword('');
      setError(null);
      props.history.push(ROUTES.PROJECTS);
    } catch (err) {
      setError(err);
    }
    event.preventDefault();
  };

  const onChange = event => {
    console.log(event.target);
    if (event.target.name === 'password') {
      setPassword(event.target.value);
    } else {
      setEmail(event.target.value);
    }
  };

  const isInvalid = password === '' || email === '';

  return (
    <form onSubmit={ onSubmit }>
      <input
        name="email"
        value={ email }
        onChange={ onChange }
        type="text"
        placeholder="Email Address"
      />
      <input
        name="password"
        value={ password }
        onChange={ onChange }
        type="password"
        placeholder="Password"
      />
      <button disabled={ isInvalid } type="submit">
        Sign In
      </button>

      {error && <p>{error.message}</p>}
    </form>
  );
};

const SignInForm = compose(withRouter, withFirebase)(SignInFormBase);

export default SignInPage;

export { SignInForm };

Last updated