My Encounter With useContext

My Encounter With useContext

UseContext hook is used to define and use global context in a react application. Suppose you are making an app that gives the user to choose a dark or light theme, So when the user is moving on to the different page of your application, how will you change background colors? UseContext here comes to the rescue, it lets you define the global context and use that anywhere in your app without passing your theme color explicitly in props each time you move to different components (pages).

Encounter with UseContext

Last week I was working on a project called bookmaarks, a place to store your resources. I wanted to show the current user name in the navbar after login, but the problem was I didn't know if the user logged out in between of change of pages(components). I didn't know how to log out users from different pages(components) or show different pages if the user is logged in.

I had a wild idea that a global context is needed to be made and there are things like redux to help. However, back when I tried to learn redux, I was overwhelmed by different things and tried not to pursue them. I had to learn the same thing again in order to complete this, so I had little fear. I reached out to a friend and he suggested me to learn about useContext hook in react. I tried that but this time with a different approach. This time, I break down the learning into smaller part and tried to implement separately.

What is UseContext?

UseContext or redux comes to help when we need to pass down data from one component to another without any immediate components in between.

So if you want to show data in showBookmark, addBookmark, Dashboard only if the user is logged in, we can achieve this by using useContext.

You don't need to pass data from one component to another as props. you can just use context API and make global context once and use it anywhere in the app.

BreakDown of Context API

Let's understand useContext API,

We need to understand three things, in order to know the context API.

  1. Creating the context object.
  2. Provider in the parent component to pass values to child components.
  3. Consumer to use the values passed down.

Example by Making Test Application

Lets create context,

import { createContext } from "react";

const dfcontx = createContext();

const appcontext = (props) => {
  const value = {
    currentUser: "khushal"
  };
  return <dfcontx.Provider value={value}>{props.children}</dfcontx.Provider>;
};

export { dfcontx };
export default appcontext;

here we are creating the context using createContext hook by const dfcontx = createContext();. In the end, we are exporting this context so we can use it in children components.

Next, we'll create a wrap children components in provider by,

<dfcontx.Provider value={value}>{props.children}</dfcontx.Provider>

We'll also wrap appcontext(provider is being returned from here) in the app js(parent file) so that we can flow this in the react tree(parent to child).

Here is what app.js file looks like,

import "./styles.css";
import Appcontext from "./nava";
import Testapp from "./testi";

export default function App() {
  return (
    <div className="App">
      <Appcontext>
        <h1>Hello CodeSandbox</h1>
        <h2>Start editing to see some magic happen!</h2>
        <Testapp />
      </Appcontext>
    </div>
  );
}

The job of app.js is done here, we can move to testi.js(filename) to finally use this context.

import { useContext } from "react";
import { dfcontx } from "./nava";

const Testapp = () => {
  const { currentUser } = useContext(dfcontx);

  return <h2>hi bro {currentUser}</h2>;
};

export default Testapp;

Here we have used the useContext hook by react to use the context we create before.

Output of Test Application

The output after all the steps is this,

image.png

Real-Life UseCase

Now you'll say, to achieve this output, we did those many steps, it's waste of time. But my friend this ain't true. We can now just add anything in place of my name and use it as context.

Let me give you peek at the real file of my project,

first one is authcontext file,

import { createContext, useEffect, useState } from "react";
import firebase from "./firebase";


const dfcontx = createContext();

const appcontext = (props) => {
  const [currentUser, setCurrentUser] = useState();
  const [loading, setLoading] = useState(true);

  function signUp() {
    var provider = new firebase.auth.GoogleAuthProvider();
    return firebase.auth().signInWithPopup(provider)
  }

  function signOut() {
    return firebase.auth().signOut();
  }

  useEffect(() => {
    const unsubscribe = firebase.auth().onAuthStateChanged((user) => {
      setCurrentUser(user);
      setLoading(false);
    });

    return unsubscribe;
  }, []);

  const value = {
    currentUser,
    signOut,
    signUp
  };
  return <dfcontx.Provider value={value}>{ !loading && props.children}</dfcontx.Provider>;
};

export { dfcontx };
export default appcontext;

Here, we have defined the context for user authentication. After this, We are getting three things currentUser, signIn, and signOut. We can use this signIn and signOut function to sign in and sign out from our app.

Now, Let us move to app.js,

import "./styles.css";
import Appcontext  from "./context";
import CompA from "./test";
import CompB from "./test2"
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";

export default function App() {
  return (
    <div className="App">
      <Appcontext>
        <h1>Test of Global Context</h1>
        <h2>Everything you need is here</h2>
        <Router>
        <Switch>
         <Route exact path="/">
          <CompA />
          </Route>
          <Route path="/auth">
          <CompB/>
          </Route>
        </Switch>
      </Router>
      </Appcontext>
    </div>
  );
}

We just wrapped the whole app.js with Appcontext So that we can pass context values in the whole react tree(parent to child).

Here is how CompA.js with signIn and signOut features looks like,

import React, { useContext } from "react";
import { dfcontx } from "./context";
import { Link } from "react-router-dom";

function CompA() {
  const { signUp, signOut } = useContext(dfcontx);

  async function handleSubmit() {
    await signUp()
    .catch(err => console.log(JSON.stringify(err)) )
  }

  return (
    <div >
      <button onClick={()=> handleSubmit()}>Sign In</button>
      <button onClick={()=> signOut()}>Sign out </button>
      <br/>
      <br/>
      <Link to="/auth">Auth</Link>

    </div>
  );
}

export default CompA;

and now CompB to check if we can share out currentUser with other child component without passing as props,

import React, { useContext } from "react";
import { Link } from "react-router-dom";
import { dfcontx } from "./context";

function CompB() {
  const { currentUser } = useContext(dfcontx);

  return (
    <div>
      {currentUser ? (
        <div>
          <h2>{currentUser.displayName}</h2>
          <img src={currentUser.photoURL} />
          <br />
          <Link to="/">Home</Link>
        </div>
      ) : (
        <div>
          <h2>User not signed in</h2>
          <Link to="/">Home</Link>
        </div>
      )}
    </div>
  );
}

export default CompB;

Output Screenshots

The output of the above code,

CompA route

image.png

Auth(CompB route) page(with sign-in)

image.png

Auth Page(after signing in)

image.png

Summary

If you read till here I am really proud of you, As you can see we can use the useContext API in the case where we need to share function or value between child components. You can check out the app I developed with this hook on bookmaarks homepage. You can see how the navbar is getting updated when the user gets logged in or logged out and even check by vising add bookmark route without signing in.

DevJoke for Happy Ending

Q. In a world where computer programs were TV shows, why would a JS program never be a reality TV show?

Ans. - because it's scripted.

Goodbye and Happy Hacking. If you have doubts or suggestions, reach out to me at Twitter.