Create Global UI State with React Context
5th Feb 2021
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.