February 19, 2021

Building a Whatsapp Web clone with React , Express and Firebase - Part 3

In this 3 part , we are going to connect the dots in our frontend app . In the last part , we ended up with our chat screen that a user is presented while logged in .

Let’s start with state management with React Context API before we jump to backend . According to react documentation ;-

Context provides a way to pass data through the component tree without having to pass props down manually at every level.

For the sake of my Redux people , will try keep the implemetation as relevant to how you would implement this using redux so as it’s easy to switch between the two .

So go ahead and create a store.js file inside the client folder and add the following :-

import React, { createContext, useReducer } from "react";

const initialState = {
  messages: [],
  loggedInUser: {},
  users: [],
  isChatSelected: false,
  selectedChatUser: {},
};

const appReducer = (state, action) => {
  switch (action.type) {
    case "SET_USER":
      return { ...state, loggedInUser: action.payload };
    case "GET_MESSAGES":
      return { ...state, messages: action.payload };
    case "ADD_MESSAGE":
      return { ...state, messages: [...state.messages, action.payload] };
    case "GET_USERS":
      return { ...state, users: action.payload };
    case "ENTER_CHAT":
      return {
        ...state,
        isChatSelected: true,
        selectedChatUser: action.payload,
      };
    default:
      return state;
  }
};

const store = createContext(null);
const { Provider } = store;

const StateProvider = ({ children }) => {
  const [state, dispatch] = useReducer(appReducer, initialState);
  return <Provider value={{ state, dispatch }}>{children}</Provider>;
};

export { store, StateProvider };

To understand exactly what we will be dealing with , i have to put the final store code and explain what is going on .

First , We will create our store using createContext hook that can pass initial value but for us we have null . createContext method is the equivalent to the createStore method of Redux .

Once a store / context is created , we can now use the Provider component to pass down the data to all children components . The provider component will pass a value prop that will have all the data that our application needs .

For our Provider value prop , we will use useReducer hook to pass our reducer function and initial state so as we can track state changes in our application using dispatch method of useReducer . The dispatch method will take a an object with type key which is the action we want to dispatch as well as an optional payload / data we want our reducer to change . From Ract Documentation here is what they say about UseReducer hook.

An alternative to useState. Accepts a reducer of type (state, action) => newState, and returns the current state paired with a dispatch method. (If you’re familiar with Redux, you already know how this works.)

Now , let’s wrap our entire app with StateProvider so as we have global access to our state . Go to App.js file and change to below: -

function App() {
  return (
    <div className="App">
      <StateProvider>
        <LoginPage />
      </StateProvider>
    </div>
  );
}

export default App;

Now we should be good to go , if you haven’t grasp the logic above , no worries , take your time and eventually it will come to light .

Let’s go on and dispatch our first action , “SET_USER” . We will do this in our LoginPage.js file , once a user is logged inside firebase.auth().onAuthStateChanged .

In LoginPage.js file , let’s import useContext hook from react as well as our store . We will then call useContext and pass our store so as we can use our dispatch function as below : -

import React, { useEffect, useState, useContext } from "react";
import { store } from "../../store";

const LoginPage = () => {
  const [dispatch] = useContext(store);

  useEffect(() => {
    firebase.auth().onAuthStateChanged((user) => {
      if (user) {
        console.log(user);
        setIsLoggedIn(true);
        dispatch({
          type: "SET_USER",
          payload: { mobile: user.phoneNumber, uid: user.uid },
        });
      } else {
        console.log("Not logged In ");
      }
      setIsLoading(false);
    });
  }, []);



}

Now , if we refresh our page we should have our user data saved in our context . To have visibility of our state , we will use React Context Dev Tool . if are using chrome , Firefox or Edge you can go on and install react-context-devtool extension . Once installed navigate to our client directory and install a package for react-context-devtool .

npm install react-context-devtool

Attach root container in debugContextDevtool method . In this , we will change our index.js file to below : -

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { debugContextDevtool } from "react-context-devtool";

const container = document.getElementById("root");

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  container
);

// Attach root container
debugContextDevtool(container);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

Now , we should be able to visualize our data as below : -

React Context Dev Tool

From the above , we can see we have a loggedinUser object with values for mobile and uid from firebase . Let’s go on and add user mobile to our sidebar : -

Go to sidebar.js file , and do below changes : -

import { useContext } from "react";
import { store } from "../../store";

const LoginScreen = () => {
      const { state } = useContext(store);
      const { loggedInUser } = state;

       <div className="sidebar__header_left">
          <IconButton>
            <AccountCircleIcon />
          </IconButton>
          {loggedInUser.mobile}
        </div>

}

And now , we should see our mobile number on top of our sidebar .

For now we are ready to start workin on our backend , but before that , we will have one thing ready . Remember Firebase Authentication will only allow us to authenticate and we can not do anything else with that data like getting user list and more , it is our responsibility to see how we can store user data once a user is logged in so as we can use it elsewhere .

To do so , when login process is succesful , we will send mobile and uid to our backend so as we can create a user there as well as get a list of all contacts in our sidebar .

So , let’s go ahead and install axios so as we can call our not yet to be created API .

npm i axios

then change firebase.auth().onAuthStateChanged to below : -

  firebase.auth().onAuthStateChanged((user) => {
      if (user) {
        console.log(user);
        setIsLoggedIn(true);
        dispatch({
          type: "SET_USER",
          payload: { mobile: user.phoneNumber, uid: user.uid },
        });
        axios
          .post("locahost:3001/user", {
            mobile: user.phoneNumber,
            uid: user.uid,
          })
          .then((response) => {
            console.log(response);
          })
          .catch((error) => {
            console.log(error);
          });
      } else {
        console.log("Not logged In ");
      }
      setIsLoading(false);
    });

From the above , when a user is authenticated , we will call locahost:3001/user to create a user . For now , no response will be returned as we don’t have our backend yet and that will be the start of our part 4 . We are finally moving on with creating the backend , see you then :-)

Buy Me A Coffee
````

Robert Rutenge
Front-end Developer | ReactJS enthusiast | Problem Solver . Find me on twitter and Github