React Context Custom Hook | The only Global State you'll ever need
Tags: react, webdev, beginners
Published: 2021-05-01 15:37:20

With a bunch of state management library out there. All you need is something you already have. React Context with the help of hooks can be your go-to for smaller project. 

Making custom hooks is one of React concept you will need to know.
Lately I have been using a custom hook to handle my global state for ease of use. 

## Custom Hook
Here is an example.

Always name your custom hook starting with `use`, so react will treat it as a hook.
#### **`useCtxDark.jsx`**
```react
import { useState, createContext, useContext, useMemo } from 'react'

const darkContext = createContext(null)

export const DarkProvider = ({ children }) => {
  const [dark, setDark] = useState(false)

  const darkValue = useMemo(() => [dark, setDark], [dark])

return 
  <darkContext.Provider value={darkValue}>
    {children} 
  </darkContext.Provider>
}

export default function useCtxDark() {
  return useContext(darkContext)
}

``` 

In our custom hook we define our `createContext()`, and a state either `useState` or `useReducer` where we store data. Then we store that data to memoize it in `useMemo` to reduce re-renders.

Notice that we have 2 exports. A named export which is gonna be our Provider that wraps our app and a default export which is our hook that is used to get and set our data.

Now we setup it up by wrapping our app with our Context Provider
#### **`App.jsx`**
```react
import { DarkProvider } from '@/hooks/useCtxDark'

export default function App() {
  return (
    // Wrapper
    <DarkProvider>
      <Nav />
      <Pages />
    </DarkProvider>
  )
}
```

Then we us it like a hook and have access to the global state where ever we call this hook. 
#### **`DarkToggle.jsx`**
```react
import useCtxDark from '@/hooks/useCtxNav'

const Nav = () => {
  const [dark, setDark] = useCtxDark()

  return <input type='checkbox' onChange={() => setDark(!dark)} />
}
```

#### Here is an example using `useReducer`
```react
import { useReducer, useContext, createContext, useMemo } from 'react'

const globalContext = createContext(null)

const initialState = {
   todo: []
}

const reducer = (state, action) => {
   switch (action.type) {
    case "ADD":
      return { todo: [...state.todo, action.payload] };
    case "DELETE":
      const filltered = state.todos.filter((x) => x.id !== action.payload)
      return {
        ...state,
        todos: [...filltered],
      }
    default:
      return state;
  }
}

export const GlobalProvider = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, initialState)

  const stateValue = useMemo(() => [state, dispatch], [state])

return 
  <globalContext.Provider value={stateValue}>
    {children} 
  </globalContext.Provider>
}

export default function useCtxDark() {
  return useContext(globalContext)
}
```
And you can make multiples of these together when you need more!
#### **`Providers.jsx`**
```react
import { DarkProvider } from '@/hooks/useCtxDark'
import { NavProvider } from '@/hooks/useCtxNav'

const Providers = ({children}) => {
  return(
    <DarkProvider>
      <NavProvider>
        {children}
      </NavProvider>
    </DarkProvider>
  )
}
```

## Multi-Context

Another version, where you make a separate context for your state and dispatch.
 
#### **`useStore.jsx`**
```react
import { useState, createContext, useContext, useMemo } from 'react'

const storeContext = createContext(null)
const dispatchContext = createContext(null)

export const StoreProvider = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, initialState)

return 
  <dispatchContext.Provider value={dispatch}>
   <storeContext.Provider value={state}>
    {children}
   </storeContext.Provider> 
  </darkContext.Provider>
}

export function useCtxStore() {
  return useContext(storeContext)
}

export function useCtxDispatch() {
  return useContext(dispatchContext)
}

``` 

Then import just the hook for store
```react
import {useCtxStore, useCtxDispatch} from './useStore'

const Component = () => {
  const {todos} = useCtxStore()
  const dispatch = useCtxDispatch()
  
  const clickHandler = (id) => {
    dispatch({type: '', payload: id})
  }

  return(
    <ul>
     {todos.map((item) => 
       <li key={item.id} onClick={() => clickHandler(item.id)}> 
         {item.name}
       </li> 
     )}
    </ul>
  )
}
``` 
#### Here is a live example with all the above hooks
{% codesandbox 6vm4v %}

If your planning to do get some asynchronous data. I do recommend that you use a library for data fetching like [React Query](https://react-query.tanstack.com/) or [SWR](https://swr.vercel.app/) for better UX and Dev Exp. 

A perfect pair to React Context for a light weight State Management for both Global and Synchronous State!