React Context with Code Snippets

When you are building web application using ReactJS, one of the problems you often face is how to pass the data from a parent to child. The simplest way to do this is passing it as a prop of child component, but when the child component is deeply nested, this leads prop drilling, which is a terrible practice. To solve this, there are solutions like React Context API and Redux.

Context API

What is Context API? Words from ReactJS official documentation.

Context provides a way to pass data through the component tree without having to pass props down manually at every level.

More official documentations can be found here

Below I share my code snippet using React Context API for theme of website. The whole code snippets are available on Github as a gist

Utility function creating data context

// utils/context.tsx

import { Context, createContext, ReactNode, Reducer, useReducer } from 'react'

export const createDataContext = <T, S>(
  reducer: Reducer<T, S>,
  actions: { [key: string]: CallableFunction },
  defaultState?: T
): {
  Context: Context<{ state: T; actions: { [key: string]: CallableFunction } }>
  Provider: typeof Provider
} => {
  const Context = createContext<{
    state: T
    actions: { [key: string]: CallableFunction }
  }>(null)

  const Provider = ({
    children,
    initialState
  }: {
    children: ReactNode
    initialState?: T
  }) => {
    const [state, dispatch] = useReducer<Reducer<T, S>>(
      reducer,
      initialState ?? defaultState
    )

    const boundActions = {}

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

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

  return { Context, Provider }
}

Reducer for Theme Context

// reducers/ThemeReducer.ts

import { Dispatch } from 'react'

export type TThemeReducerType = 'SET_THEME'

export type TThemeState = {
  theme: 'light' | 'dark'
}

export interface IThemeAction {
  type: TThemeReducerType
  payload?: IThemeState
}

export const initialThemeState: IThemeState = {
  theme: 'light'
}

export const themeReducer = (
  state: TThemeState,
  action: IThemeAction
): TThemeState => {
  switch (action.type) {
    case 'SET_THEME':
      return { ...action.payload }
    default:
      return state
  }
}

export const setTheme =
  (dispatch: Dispatch<IThemeAction>) =>
  (theme: TThemeState): void =>
    dispatch({
      type: 'SET_THEME',
      payload: theme
    })

Theme Context

// contexts/ThemeContext.tsx

import {
  themeReducer,
  IThemeAction,
  IThemeState,
  initialThemeState,
  setTheme
} from '@/reducers/ThemeReducer'
import { createDataContext } from '@/utils/context'

export const { Context: ThemeContext, Provider: ThemeProvider } =
  createDataContext<IThemeState, IThemeAction>(
    themeReducer,
    { setTheme },
    initialThemeState
  )

How to use it

Wrap components somewhere in the high level

import { ThemeProvider } from '@/contexts'

<ThemeProvider initialState={initialState}>
  ...
<ThemeProvider>

And in the child component where you want to access context state or update the state...

import { useContext, useMemo, useState } from 'react'
import { ThemeContext } from '@/contexts'
import { IThemeState } from '@/reducers/themeReducer'

const {
  state: theme,
  actions: { setTheme }
} = useContext(ThemeContext)

Looks pretty straightforward, ha? 😎

Redux

Redux is a predictable state container for JavaScript apps. You can use Redux together with React, or with any other view library. It is tiny (2kB, including dependencies), but has a large ecosystem of addons available.

Redux is an Open Source Library which provides a central store, and actions to modify the store. It can be used with any project using JavaScript or TypeScript.

I am not going to share long code snippets for redux here(no worries 🤣), but maybe I will do in another article if I find time to write it!

But I write down the steps to use Redux in your ReactJS apps:

  • Create a Reducer
  • Configure the Store
  • Make the Store available for data consumption
  • Use State or Dispatch Actions

It seems not to be a long list, but having Redux integrated in your ReactJS application can be.

Redux is powerful, extendible and framework agnostic, but requires expensive setup to use. So please make sure that you need it when you are going to use it. In many cases, Context is enough to pass data to child components. And even with Redux integrated, Context is really handy for small things, its worth thinking for a few seconds before you choose between Context & Redux.

Got bored? No more long code snippets, just scroll down a bit more to have a fun! 😂

context vs redux

Summary

Context API is a light-weight solution which is more suited for passing data from a parent to a deeply nested child and Redux is a more robust solution for global State Management.