Context + Hooks > React + Redux
Tags: react, reactnative, redux
Published: 2021-06-24 10:50:43

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>
);
```

And a `Consumer` uses the values:
```
const ConsumerComponent = () => (
  <StateContext.Consumer>
    {value => (
      <Button primaryColor={{ value.color }}>Consumer</Button>
    )}
  </StateContext.Consumer>
);
```

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](https://reactjs.org/docs/context.html)

...

#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>
  );
} ;
```

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)
```

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>
  );
}
```


####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()
}
```

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.