We can intitalize a context userContext
as shown below:
const userContext=react.createContext()
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.
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
}
}
<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
{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
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.
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