I’m a big fan of the Syntax.fm podcast and I recently did the Level Up Tutorials Custom React Hooks course. It was really good and definitely helped me gain a deeper understanding of React Hooks, plus some best practices and code organisation techniques.
In particular, Lesson #12 (Custom Hooks for Context Providers) details this method for organising and using React Context.
Basically, it is a simple pattern that lets us import and use our app state like this:
import { useAppState } from '../state';
const { isMenuOpen, toggleMenu } = useAppState();
No need to import useContext
every time!
We start by writing the hook.
This is the only time you need to use either createContext
or useContext
.
src/state/PageWrapper.js
import React, { createContext, useContext } from 'react';
import { useToggle } from '../hooks';
export const AppContext = createContext({
isMenuOpen: false
});
export const PageWrapper = ({ children }) => {
const { isToggled, toggle } = useToggle(false);
return (
<AppContext.Provider
value={{
isMenuOpen: isToggled,
toggleMenu: toggle
}}
>
{children}
</AppContext.Provider>
);
};
export const useAppState = () => useContext(AppContext);
As long as our exports above are named appropraitely, this gives us a nice way of accessing all our state hooks in one place.
src/state/index.js
export * from "./PageWrapper";
We need to wrap our App object in the PageWrapper:
src/state/index.js
import { PageWrapper } from "./state";
function App() {
return (
<PageWrapper>
<div>
<Header>
<Menu />
<h1>Header</h1>
// etc
</Header>
</div>
</PageWrapper>
Now we can use it in any child component:
import React from 'react';
import { useAppState } from '../state';
const Nav = () => {
const { isMenuOpen, toggleMenu } = useAppState();
if (!isMenuOpen) return null;
return (
<nav
style={{
background: 'var(--black)',
color: 'white',
position: 'fixed',
width: '100vw',
height: '100vh',
left: 0,
right: 0
}}
>
<h1>Hello</h1>
<button onClick={toggleMenu}>Close</button>
</nav>
);
};
export default Nav;