CARVIEW |
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
:
-
Action
: a JavaScript object that contains information about thereducer
and tells it what to do. -
initialState
: defines the initial state ofuseReducer()
. -
currState
: defines the current state of the component. -
reducer
: accepts the current state and action and then returns a new state that goes touseReducer
ascurrState
based on a particular action. -
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
- 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 };
- We write our
reducer
function to deal with thestate
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;
}
};
- We build a provider component and wrap
AppContext.Provider
with the children props and write ourdispatch
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.
Relevant Answers
Explore Courses
Free Resources