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 the reducer
and tells it what to do.
initialState
: defines the initial state of useReducer()
.
currState
: defines the current state of the component.
reducer
: accepts the current state and action and then returns a new state that goes to useReducer
as currState
based on a particular action.
dispatch
: tells duties (action) to the reducer to perform certain tasks.
reducer
functionThe 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
}
}
dispatch
functionAs 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
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
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"
import React, { useContext, useReducer } from "react";
const AppContext = React.createContext();
const initialState = { isModalOpen: false, isSidebarOpen: false };
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;
}
};
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
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.