DEV Community

Cover image for Context + Hooks > React + Redux
Anubhav
Anubhav

Posted on

Context + Hooks > React + Redux

Amongst the dev community, one of the most hot and trending technology these days has got to be React and regardless of how you feel about working with it, you have to give it credit for at-least feeling like a full-blown framework despite being a library.
Redux has been the library of choice for global state management for years now but it's high time now that we move forward. The primary reason for this being the verbosity of Redux that not just make it a tad cumbersome to use but also kind of senseless unless you are working on an enterprise-level project.

CONTEXT API

React 16 gave us a wonderful solution to global state management => Context API. A library with syntax very similar to Redux but with exponentially less boilerplate and setup.
Using Context is pretty straightforward. A Provider serves the values:

import React from 'react';
const StateContext = React.createContext(/* default state */);
const App = props => (
  <StateContext.Provider value={{ color: green }}>
    {props.children}
  </StateContext.Provider>
);
Enter fullscreen mode Exit fullscreen mode

And a Consumer uses the values:

const ConsumerComponent = () => (
  <StateContext.Consumer>
    {value => (
      <Button primaryColor={{ value.color }}>Consumer</Button>
    )}
  </StateContext.Consumer>
);
Enter fullscreen mode Exit fullscreen mode

This implementation shall, however, work only for functional components. There are other ways to access context values in class-based components. You may read up on those in the official React docs

...

HOOKS

Soon after the Context API was released in production, hooks were introduced and things were never the same again. It was not just state management that benefited but React in its entirety saw a much-needed overhaul from all the classes that we used to write previously.
Out of the 10 default hooks that React ships with, the useReducer hook can be used to set up Context API within our app. The useReducer hook takes 2 arguments; namely a reducer function and an initial state:

import React, { useReducer } from 'React';

const reducer = (state, action) => {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };

    default:
      return state;
  }
};

const IncrementComponent = ({ initialCount }) => {
  const [state, dispatch] = useReducer(reducer, { count: 0 });  return (
    <button onClick={() => dispatch({ type: 'increment'})}>
      Increment: {state.count}
    </button>
  );
} ;
Enter fullscreen mode Exit fullscreen mode

This syntax looks awfully similar to Redux. Cool right!
The useReducer hook returns the [state, dispatch] array where state is the current state you can consider dispatch to be like a gun that shoots actions at our data layer. You can call it anything but while looking at a Redux substitute, we can use its naming convention at the very least!
Every time the dispatch method is called, the reducer function is triggered which matches your action type and updates the state as per the payload you send to it.

Now for the fun part!

HOOKS + CONTEXT

Using Context and hooks together allows you to make your own global state management system in hardly 10 lines of code. Intrigued right because that would be pretty neat!

import React , { createContext , useContext , useReducer } from 'react';

export const StateContext = createContext();

export const StateProvider=({ reducer , initialState , children })=>(
    <StateContext.Provider value = {useReducer(reducer,initialState)}>
        {children}
    </StateContext.Provider>
);

export const useStateValue = () => useContext(StateContext)
Enter fullscreen mode Exit fullscreen mode

Et voila. We're done! Let's see what's happening here:

  • createContext creates a context object and returns it to StateContext which stores the Provider and the Consumer
  • Next we create StateProvider which is basically a HOC which wraps its children with the values offered by the Provider.
  • Finally we create useStateValue which is a custom hook which uses the StateContext context. We did this because otherwise you would have to import useContext and StateContext in all places wherever you wanted access to these values. Building up on the DRY principle of coding, we created a universal hook to take care of the same once and for all.

...
Next we just need a reducer and an initial state to start using the StateProvider component. We already took a look at what a reducer could look like earlier. The initial state can be anything you want; a JSON object, an array, or whatever fits your purposes the best.

Now let's see how to use the StateProvider component and we're all set. In our entry file (generally App.js), we just need to wrap our return block with the StateProvider =>

import { StateProvider } from './utils/StateProvider';
import reducer, { initialState } from './utils/reducer';

const App = () => {
  return (
    <StateProvider initialState={initialState} reducer={reducer}>
        // Content ...
    </StateProvider>
  );
}
Enter fullscreen mode Exit fullscreen mode

SIDE NOTE

  • To now access the state values inside your component, you simply go like:
import { useStateValue } from '../utils/StateProvider'

const MyComponent = () => {
    const [state, dispatch] = useStateValue()
}
Enter fullscreen mode Exit fullscreen mode

You can always use object destructing to just get the required slices of data from your global state instead of getting the entire state.

  • This implementation is viable only for functional components because useStateValue under the hood use the useContext hook and the rules of hooks clearly state that hooks can only be called inside the body of a functional component. If you want to use these values in a class-based component, you shall have to go over the bit more verbose method of using Consumer or contextType but in modern times, working with functional components is the norm and there's no way that this implementation shall break any time in the future.

Latest comments (3)

Collapse
 
rmaurodev profile image
Ricardo • Edited

Nice article @anukr98

Collapse
 
fandyajpo profile image
Fandoy

i love the explanation sir, btw im using context and reducer for my state management. would u mind to make more article about react ecosystem??

for example preact for nextjs, etc :)

Collapse
 
anukr98 profile image
Anubhav

Hey @fandyajpo
So glad that you loved the explanation.
And please feel free to drop down in the comments what else you would like to know and I'll give it my best to simplify it for you as well as I can.