What is context API?

What is a context?

A context is a method designed to pass data through the component tree without having to use prop drilling (i.e. manually pass props down at every component level).

We can intitalize a context userContext as shown below:

const userContext=react.createContext()

UseReducer

UseReducer is similar to useState and is used as an alternative to useState. UseReducer accepts a reducer function with the parameters (state, action) => newState and returns the current state assigned with a dispatch method.

const [currState, dispatch]=useReducer(reducer,initialState)

Let’s define terms from useReducer:

  1. Action: a JavaScript object that contains information about the reducer and tells it what to do.

  2. initialState: defines the initial state of useReducer().

  3. currState: defines the current state of the component.

  4. reducer: accepts the current state and action and then returns a new state that goes to useReducer as currState based on a particular action.

  5. dispatch: tells duties (action) to the reducer to perform certain tasks.

The reducer function

The reducer function accepts the current state and action and then returns a new state that goes to useReducer as a currState based on a particular action. Until we perform an action on useReducer, the currentState is the initial state passed into UseReducer() as an argument.

initialState=0:
const [currState, dispatch] = useReducer (reducer,initialState);

const reducer=(state,action)=>{
switch (action.type){
   case "Increment":
    return state+1; //state=1
  case "Decrement": 
    return state-1; //State decrements back to zero
 case default: 
    return state

}
}

The dispatch function

As the name implies, we tell duties (action) to the reducer to perform certain tasks.

<button onClick={()=>dispatch({type:"increment")}>Increment</button>
//This increases our global state by 1. 

The component after dispatch:

<div> count -{currState} </div>
//Current state displays 1

Values/payloads and complex states

Payloads or value

We define action to be a JS object at first; however, it can be a single value without types. Payloads help us use the action object to pass in values to the state computation.

{type:"increment", value:1}
//this is usually how our action object looks like where we 
//have an action.type and action.value as the object keys.
 case "Increment":
    return state+action.value; //Where action.value=1
    // This returns the value of the state +1 

Complex states in the reducer

We define useReducer and set the intialState that will be accessed and edited by the reducer as state.

const initialState={ firstCounter:0, secondCounter:10};
const [count, dispatch]=useReducer(reducer,initialState)

We use the reducer function and access the initialState and start our state manipulation.

const reducer=(state,action)=>{
if(action.type==="increment"){
   return {...state, firstCounter:state.firstCounter+action.value};
}
if(action.type==="decrement"){
   return {...state, firstCounter:state.firstCounter-action.value};
}
return state;
}

Let’s test our code.

<button onClick={()=>dispatch({type:"increment")}>Increment</button>
//This increases our first counter by 1. 

useReducer and useContext

Bear in mind that the context is generally used to globally access our state without prop drilling. useReducer is used for state management just like useState, but we deal with actions and dispatch here.

First, we create our context in the context folder:

import React, { useState, useContext } from "react";
const AppContext = React.createContext();

const AppProvider = ({ children }) => {
  return <AppContext.Provider value="hello">{children}</AppContext.Provider>;
};


export { AppContext, AppProvider };

We wrap our Provider around in the react index.js file:

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import { AppProvider } from "./context";
import App from "./App";
ReactDOM.render(
  <React.StrictMode>
    <AppProvider>
      <App />
    </AppProvider>
  </React.StrictMode>,
  document.getElementById("root")
);

This makes it possible for us to access the value of the context from anywhere in the app since the whole app is wrapped with a provider.

We access our context as as follows:

const data = useContext(AppContext);
//console.log(data) =>"hello"

How to build a sidebar and modal with context

  1. We import all the required components and initialize our state value.
import React, { useContext, useReducer } from "react";
const AppContext = React.createContext();

const initialState = { isModalOpen: false, isSidebarOpen: false };
  1. We write our reducer function to deal with the state value when we open or close a modal or sidebar.
const reducer = (state, action) => {
  //Defines all the states
  switch (action.type) {
    case "OPEN_MODAL":
      return { ...initialState, isModalOpen: true };
    case "OPEN_SIDEBAR":
      return { ...initialState, isSidebarOpen: true };
    case "CLOSE_MODAL":
      return { ...initialState, isModalOpen: false };
    case "CLOSE_SIDEBAR":
      return { ...initialState, isSidebarOpen: false };
    default:
      return state;
  }
};
  1. We build a provider component and wrap AppContext.Provider with the children props and write our dispatch function, which sends an action to open and close the modal.

In this context, we store each dispatch inside a function, which is then passed to the value in the provider that can be accessed anywhere in our component tree. We also initialize useReducer and parse in all the necessary input parameters.

const AppProvider = ({ children }) => {
  //Initial State
  const initialState = { isModalOpen: false, isSidebarOpen: false };
  const [state, dispatch] = useReducer(reducer, initialState);

  //various functions
  const openSidebar = () => {
    dispatch({ type: "OPEN_SIDEBAR" });
  };
  const closeSidebar = () => {
    dispatch({ type: "CLOSE_SIDEBAR" });
  };
  const openModal = () => {
    dispatch({ type: "OPEN_MODAL" });
  };
  const closeModal = () => {
    dispatch({ type: "CLOSE_MODAL" });
  };
  return (
    <AppContext.Provider
      value={{
        isModalOpen: state.isModalOpen,
        isSidebarOpen: state.isSidebarOpen,
        closeModal,
        openModal,
        closeSidebar,
        openSidebar,
      }}
    >
      {children}
    </AppContext.Provider>
  );
};

The values of the Provider (i.e. the state of our modal, and functions that open and close the modal) can now be accessed anywhere in the component tree without prop drilling.

import React, { useContext } from "react";
import { FaBars } from "react-icons/fa";
import { AppContext, useGlobalContext } from "./context";
const Home = () => {
   const { openSidebar, openModal }  = useContext(AppContext);
  return (
    <main>
      <button className="sidebar-toggle" onClick={openSidebar}>
        <FaBars />
      </button>
      <button className="btn" onClick={openModal}>
        show modal
      </button>
    </main>
  );
};

The code above toggles in the state and dispatches the turn on or off function of the state in the modal.

We come across this in the Provider value of the context component.

        isModalOpen: state.isModalOpen,
        isSidebarOpen: state.isSidebarOpen

In the code above, we get the state object and check its value at every point in time.

 const state = { isModalOpen: false, isSidebarOpen: false };
isModalOpen: state.isModalOpen //which produces false at this code instance

Conclusion

Context can be applied in various ways and the variations may be slightly different from what I have here, but hopefully, this shot makes it easy for anyone to grasp context API and its methodologies.