Create Global UI State with React Context

2021-02-05T00:00:00.000Z

alt

How do you implement some UI state which can be changed from different parts of your React application?

In this example I implement a menu which can be toggled using a central UI context. I've built it in such a way that a central Provider component controls how this context will be updated. By doing it this way you will keep your code easy to read and will reduce the complexity of any bugs you encounter in the future.

I walk through the following:

  • converting some functionality in useState to context
  • creating a React Context
  • exposing the React Context as a hook
  • creating a Provider component

The elements of code I go through

The NextJs home page now gets the menu state from the useUIContext hook. This hook can be accessed from any other page or component within the application.

I have simplified the code in the snippet below. It shows that we get the isMenuOpen property from the UIContext object and the className then uses it to set the block or hidden classes.

import { useUIContext } from "../src/UIContext";
export default function Home() {
const { isMenuOpen, toggleMenu } = useUIContext()
// ...
return (
// ...
<aside className={`${isMenuOpen ? 'block' : 'hidden'} ...`}>
// ...
)
}

We can access the state from our UIContext because in this NextJs application I have wrapped the page component in our UIContextProvider. This is done within the pages/_app.js file.

import '../styles/globals.css'
import { UIContextProvider } from '../src/UIContext'
function MyApp({ Component, pageProps }) {
return (
<UIContextProvider>
<Component {...pageProps} />
</UIContextProvider>
)
}
export default MyApp

The UIContext.js file here shows the final result. You can easily add more properties to the state as well as more functions to manupulate it in a controlled way.

import { createContext, useContext, useState, useMemo } from 'react'
const UIContext = createContext(null)
export const useUIContext = () => useContext(UIContext)
export const UIContextProvider = ({ children }) => {
const [state, setState] = useState({
isMenuOpen: false
})
const value = useMemo(() => {
const toggleMenu = () => setState({
...state,
isMenuOpen: !state.isMenuOpen
})
return {
...state,
toggleMenu
}
}, [state])
return (
<UIContext.Provider value={value}>
{children}
</UIContext.Provider>
)
}

I've had a few questions from people recently about how this works so I hope this has been useful to you.