Creating Context for Your Next React App

How to Abstract A Context Provider for the useContext Hook

Kenny Marks
The Startup
4 min readAug 2, 2020

--

Hooks are a great way to manage state in a React Web Application. If you are familiar with hooks you are likely familiar with the useContext hook. If not this hook is very similar to the Redux library in that it can provide all components access to state. In this article, I will be discussing how to create a generalized context provider file that will allow you to abstract state logic from your components.

The first step in creating a Context Provider is to create the file. In your file directory create a file named something like:

createDataContext.js

Now in the new file our next step is to import ‘React’ and the ‘useReducer’ hook from ‘react’

import React, {useReducer} from 'react'

Next this file will export a function that will taken in three arguements: a reducer, actions, and an inital state. The all three of these arguements will in turn come from a specfic context file. I am not going too many details as to how to build this context file but at the end of this article I will provide a general flow for how it could look.

import React, {useReducer} from 'react'export default (reducer, actions, initialState) => {}

Inside of this function we are now going to do several things first we are going to create our ‘Context’ variable by calling the ‘createContext’ function on “React”:

import React, {useReducer} from 'react'export default (reducer, actions, initialState) => {
const Context = React.createContext()
}

With our ‘Context’ created we can now create our ‘Provider.’ The ‘Provider’ is a React component and as all react components it will return series of JSX. This component will recevie one prop “children” which will acutally be every component that’s wrapped inside of our provider. It will also the ‘useReducer hook’ to create the state. It will also create a boundActions object which will allow our conext to be able to alter an applicaitons state.

import React, {useReducer} from 'react'export default (reducer, actions, initialState) => {
const Context = React.createContext()

const Provider = ({children}) =>{
const [state, dispatch] = useReducer(initialState)

const boundActions = {}
for (let key in actions) {
boundActions[key] = actions[key](dispatch)
}

return (
< Context.Provider value={{ state, ...boundActions }} >
{children}
</ Context.Provider >
)
}
}

Finally we just need to return our Context and Provider so that our various context files can create and export their states.

import React, {useReducer} from 'react'export default (reducer, actions, initialState) => {
const Context = React.createContext()

const Provider = ({children}) =>{
const [state, dispatch] = useReducer(initialState)

const boundActions = {}
for (let key in actions) {
boundActions[key] = actions[key](dispatch)
}

return (
< Context.Provider value={{ state, ...boundActions }} >
{children}
</ Context.Provider >
)
}
return [Context. Provider]}

With that we have completed our ‘createDataContext’ file. With one more step we can then begin accessing state within our app. Now all we need to do is import a provider (in this case ‘ExampleProvider’) and wrap it around the app in index.js:

import React from 'react';
import ReactDOM from 'react-dom';
import {ExampleProvider} from './src/context/ExampleProvider'
import App from './App';
ReactDOM.render( <ExampleProvider>
<React>
<App />
</React>,
</ ExampleProvider>
document.getElementById('root')
);

Example Context:

Below is an Example context that createds a rreducer. That reducer adds a random number id and and value to state, removes it from state, and allows for those numbers to be edited. And it also creates functions that call on each case within its reducer. Finally it exports everything by calling on createDataContext.

Generic Example Context:import createDataContext from './createDataContext'const exampleReducer = (state, action) => {
switch (action.type) {
case 'edit_example':
return state.map((example) => {
return example.id === action.payload.id ? action.payload : example
})
case 'delete_example':
return state.filter((example) => example.id !== action.payload)
case 'add_example':
console.log(state)
return [
...state,
{
{
id: Math.floor(Math.random() *99999),
value: Math.floor(Math.random() *99999
}
}
]
default:
return state
}
}
const addExample = (dispatch) =>{
return (title, content, callback) =>{
dispatch({type: 'add_blogpost', payload: {title, content}})
}
const deleteExample = (dispatch) => {
return (id) => (
dispatch({type: 'delete_blogpost', payload: id})
)
}
const editExample = (dispatch) => {
return (id, title, content, callback) => {
dispatch({
type: 'edit_blogpost',
payload: {id, title,content}
})
}
export const {ExampleContext, ExampleProvider} = createDataContext(
exampleReducer,
{addExample,deleteExample,editExample },
[{id: 1, value: 1 }]
)

With this we have created an specific file which can abstract some of the logic away from creating state from each context file. Happy Coding!

--

--

Kenny Marks
The Startup

A full stack developer with an interest in Cybersecurity, International Relations, and Gaming.