Use Hooks + Context, not React + Redux
Tags: react, redux, javascript, productivity
Published: 2019-11-26 15:45:27

---
title: Use Hooks + Context, not React + Redux
published: true
date: 2019-11-25 19:30:47 UTC
tags: react,redux,javascript,productivity
canonical_url: https://blog.logrocket.com/use-hooks-and-context-not-react-and-redux/
cover_image: https://thepracticaldev.s3.amazonaws.com/i/rum3hq3g11gouopcby1i.jpeg
---
**Written by [Ebenezer Don](https://blog.logrocket.com/author/ebenezerdon/)**✏️

Redux introduces a lot of complexity to our codebase with the excessive amount of code it requires. At best, this makes it an imperfect solution for state management in React applications. And yet, far too many React developers default to Redux for state management without considering other alternatives.

In this article, 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.

## Why we need a state management tool

In typical React, the way to handle data between disconnected components is through prop drilling. Since there is no global state that components can access if, for instance, you want to pass data from a top-level component to a fifth-level component, you’ll have to pass the data as a prop on each level of the tree until you get to your desired component.

This results in writing a ton of extra code, and giving components properties that they will never use also affects their architectural design. In order to solve this problem, we needed a way to provide a global state that all components, no matter how deeply nested they are, could access.

By solving this, Redux, an open-source JavaScript library for managing application state, became the go-to solution for React developers.

[![LogRocket Free Trial Banner](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/f760c-1gpjapknnuyhu8esa3z0jga.png?resize=1200%2C280&ssl=1)](https://logrocket.com/signup/)

## How Redux works

The [Redux documentation](https://redux.js.org/introduction/getting-started) describes it as a predictable state container for JavaScript applications that helps us to write applications that behave consistently, run in different environments, and are easy to test.

One disadvantage of prop drilling is the need for writing a considerable amount of extra code in order to access data from a top-level component. With Redux, this disadvantage is felt even more as a lot of complexity comes with all its extra code required for setting up a global state for our application. Redux requires three main building parts to function: actions, reducers, and store.

### Actions

These are objects that are used to send data to the Redux store. They typically have two properties: a type property for describing what the action does and a payload property that contains the information that should be changed in the app state.

```jsx
// action.js
const reduxAction = payload => {
  return {
    type: 'action description',
    payload
  }
};

export default reduxAction;
```

The `type` is usually in all caps, with its words separated by underscores. For example, `SIGNUP_USER` or `DELETE_USER_DATA`.

### Reducers

These are pure functions that implement the action behavior. They take the current application state, perform an action, and then return a new state:

```jsx
const reducer = (state, action) => {
  const { type, payload } = action;
  switch(type){
    case "action type":
      return {
        ["action description"]: payload
      };
    default:
      return state;
  }
};

export default reducer;
```

### Store

The store is where the application’s state is housed. There is only one store in any Redux application:

```jsx
import { createStore } from 'redux'

const store = createStore(componentName);
```

Since our application can only have one Redux store, in order to create a single root reducer for all our components, we’ll need the [`combineReducers`](https://redux.js.org/api/combinereducers) method from Redux.

With this long process and considerable amount of code required to set up Redux, imagine what our codebase will look like when we have multiple components to work with. Even though Redux solves our state management problem, it is really time-consuming to use, has a difficult learning curve, and introduces a whole new layer of complexity to our application.

Fortunately, the React Context API solves this problem. When combined with React Hooks, we have a state management solution that is less time-consuming to set up, has an easy learning curve, and requires minimal code.

## The React Context API

The new Context API came with React 16.3. Here’s how Context is explained in the [React documentation](https://reactjs.org/docs/context.html):

> 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:

```jsx
import React from 'react';

const newContext = React.createContext({ color: 'black' });
```

The `createContext` method returns an object with a `Provider` and a `Consumer` component:

```jsx
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:

```jsx
<Provider value={color: 'blue'}>
  {children}
</Provider>
```

The `Consumer`, as its name implies, consumes the data from the `Provider` without any need for prop drilling:

```jsx
<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.

## What are Hooks in React?

Hooks are a type of function that enables the execution of custom code in a base code. In React, Hooks are special functions that allow us to “hook into” its core features.

React Hooks provide an alternative to writing class-based components by allowing us to easily handle state management from functional components.

### The `useContext` Hook

If you noticed, when explaining the React Context API, we needed to wrap our content in a `Consumer` component and then pass a function as a child just so we could access (or consume) our state. This introduces unnecessary component nesting and increases the complexity of our code.

The `useContext` Hook makes things a lot nicer and straightforward. In order to access our state using it, all we need to do is call it with our created `context` as its argument:

```jsx
const newContext = React.createContext({ color: 'black' });

const value = useContext(newContext);

console.log(value); // this will return { color: 'black' }
```

Now, instead of wrapping our content in a `Consumer` component, we can simply access our state through the `value` variable.

### The `useReducer` Hook

The `useReducer` Hook came with React 16.7.0. Just like the `reduce()` method in JavaScript, the `useReducer` Hook receives two values as its argument — in this case, the current state and an action — and then returns a new state:

```jsx
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:

```jsx
...
return (
  <button onClick={() =>
    dispatch({ type: 'action type'})}>
  </button>
)
```

## The `useReducer` Hook plus the Context API

### Setting up our store

Now that we know how the Context API and the `useReducer` Hook work individually, let’s see what happens when we combine them in order to get the ideal global state management solution for our application. We’ll create our global state in a `store.js` file:

```jsx
// 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 we 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.

Notice how we used the `useReducer` Hook in our `StateProvider`. 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:

```jsx
// 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:

```jsx
// 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:

```jsx
// 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

To a good extent, 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.

* * * 
**Editor's note:** Seeing something wrong with this post? You can find the correct version [here](https://blog.logrocket.com/use-hooks-and-context-not-react-and-redux/).

## Plug: [LogRocket](https://logrocket.com/signup/), a DVR for web apps
 
![LogRocket Dashboard Free Trial Banner](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png?resize=1200%2C677&ssl=1)
 
[LogRocket](https://logrocket.com/signup/) is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
 
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.
 
[Try it for free](https://logrocket.com/signup/).
* * *
The post [Use Hooks + Context, not React + Redux](https://blog.logrocket.com/use-hooks-and-context-not-react-and-redux/) appeared first on [LogRocket Blog](https://blog.logrocket.com).