Use Hooks + Context, not React + Redux
Tags: redux, react, hooks, contextapi
Published: 2021-01-08 17:52:46

**Redux introduces a lot of complexity to our codebase with the excessive amount of code it requires. This makes it an imperfect solution for state management in React applications.**
I will introduce the **React Context API** for state management and show you what makes **React Hooks plus the Context API a better solution than Redux**.

**The React Context API**
_Context provides a way to pass data through the component tree without having to pass props down manually at every level._

_The React Context API is React’s way of managing state in multiple components that are not directly connected._

_To create a context, we’ll use the `createContext` method from React, which accepts a parameter for its default value:_
```javascript
import React from 'react';
const newContext = React.createContext({ color: 'black' });
```
The `createContext` method returns an object with a Provider and a Consumer component:
```javascript
const { Provider, Consumer } = newContext;
```
The `Provider` component is what makes the state available to all child components, no matter how deeply nested they are within the component hierarchy. The `Provider` component receives a value **prop**. This is where we’ll pass our current value:
```javascript
<Provider value={color: 'blue'}>
  {children}
</Provider>
```
The `Consumer`, as its name implies, consumes the data from the `Provider` without any need for _prop drilling(threading)_:
```javascript
<Consumer>
  {value => <span>{value}</span>}}
</Consumer>
```
Without Hooks, the Context API might not seem like much when compared to Redux, but combined with the `useReducer` Hook, we have a solution that finally solves the state management problem.

**The `useReducer` Hook**
he `useReducer` Hook came with React 16.8. Just like the reduce() method in JavaScript, the `useReducer` Hook receives two values as its argument — _a reducer function_ and _an initial state_ — and then _returns a new state_:
```javascript
const [state, dispatch] = useReducer((state, action) => {
  const { type } = action;
  switch(action) {
    case 'action description':
      const newState = // do something with the action
      return newState;
    default:
      throw new Error()
  }
}, []);
```
In the above block, we’ve defined our state and a corresponding method, `dispatch`, handling it. When we call the `dispatch` method, the `useReducer()` Hook will perform an action based on the `type` that our method receives in its action argument:
```javascript
...
return (
  <button onClick={() =>
    dispatch({ type: 'action type'})}>
  </button>
)
```

**The `useReducer` Hook plus the Context API**
let’s see what happens when we combine `useReducer` with `Context API` in order to get the ideal global state management solution for our application.
We’ll create our global state in a store.js file:
```javascript
// store.js
import React, {createContext, useReducer} from 'react';

const initialState = {};
const store = createContext(initialState);
const { Provider } = store;

const StateProvider = ( { children } ) => {
  const [state, dispatch] = useReducer((state, action) => {
    switch(action.type) {
      case 'action description':
        const newState = // do something with the action
        return newState;
      default:
        throw new Error();
    };
  }, initialState);

  return <Provider value={{ state, dispatch }}>{children}</Provider>;
};

export { store, StateProvider }
```

In our store.js file, we used the `createContext()` method from React that is explained earlier to create a new context.
Remember that the `createContext()` method returns an object with a `Provider` and `Consumer` component. This time, we’ll be using only the `Provider` component and then the `useContext` Hook when we need to access our state.
When we need to manipulate our state, we’ll call the `dispatch` method and pass in an object with the desired `type` as its argument.
In our `StateProvider`, we returned our `Provider` component with a value `prop` of state and `dispatch` from the `useReducer` Hook.

**Accessing our state globally**
In order to access our state globally, we’ll need to wrap our root `<App/>` component in our `StoreProvider` before rendering it in our `ReactDOM.render()` function:
```javascript
// root index.js file
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { StateProvider } from './store.js';

const app = (
  <StateProvider>
    <App />
  </StateProvider>
);
ReactDOM.render(app, document.getElementById('root'));
```

Now, our `store` context can be accessed from any component in the component tree. To do this, we’ll import the `useContext` Hook from `react` and the `store` from our ./store.js file:
```javascript
// exampleComponent.js
import React, { useContext } from 'react';
import { store } from './store.js';

const ExampleComponent = () => {
  const globalState = useContext(store);
  console.log(globalState); // this will return { color: red }
};
```

**Adding and removing data from our state**
We’ve seen how we can access our global state.
In order to add and remove data from our state, we’ll need the `dispatch` method from our `store` context. We only need to call the `dispatch` method and pass in an object with type (the `action` description as defined in our `StateProvider` component) as its parameter:
```javascript
// exampleComponent.js
import React, { useContext } from 'react';
import { store } from './store.js';

const ExampleComponent = () => {
  const globalState = useContext(store);
  const { dispatch } = globalState;

  dispatch({ type: 'action description' })
};
```

**Conclusion**

Redux works for state management in React applications and has a few advantages, but its verbosity makes it really difficult to pick up, and the ton of extra code needed to get it working in our application introduces a lot of unnecessary complexity.

On the other hand, with the `useContext` API and React Hooks, there is no need to install external libraries or add a bunch of files and folders in order to get our app working. This makes it a much simpler, more straightforward way to handle global state management in React applications.