Redux VS React Context: Which one should you choose?
Tags: react, redux, context, beginners
Published: 2020-01-02 17:22:55

Originally posted on [my blog](https://www.ibrahima-ndaw.com/blog/redux-vs-react-context-which-one-should-you-choose/) <hr>
React context has been there for a while. With the coming of React hooks, it's now much better. It has so many advantages, including the fact that the context API doesn't require any third-party libraries. We can use it in React apps to manage our state like redux.<br>
In this article, we're going to manage our state with React context, to see by ourselves if it's better than redux regarding the state's management. By the way, this post is the follow-up of my previous one.<br>
{% link ibrahima92/7-steps-to-understand-react-redux-121j %}
**Note**: This article covers only the context API. We're going to build the same project with React context. If you're interested in how to manage state with redux, my previous post might help you [here](https://www.ibrahima-ndaw.com/blog/7-steps-to-understand-react-redux/).<br>

Otherwise let's get started.
- [Prerequisites](#prerequisites)
- [Setting Up the Project](#setting-up-the-project)
- [Create a context](#create-a-context)
- [Provide the context](#provide-the-context)
- [Consume the context](#consume-the-context)
- [Enhance the context with useReducer](#enhance-the-context-with-usereducer)
- [Redux VS the React Context: Who wins?](#redux-vs-the-react-context-who-wins)

## Prerequisites
To be able to follow along, you have to know at least the basics to advance features of React and particularly React hooks. A good grasp of redux can also help.

## Setting Up the Project
If you're good to go, we can now create a new React app by running:

```shell
npx create-react-app react-context-hooks-example
```
Then, we have to create some files.

- Add a `containers` folder in the `src`, then create `Articles.js` file.

```jsx
import React, { useState } from "react"
import Article from "../components/Article/Article"

const Articles = () => {
  const [articles, setArticles] = useState([
    { id: 1, title: "post 1", body: "Quisque cursus, metus vitae pharetra" },
    { id: 2, title: "post 2", body: "Quisque cursus, metus vitae pharetra" },
  ])

return (
    <div>
      {articles.map(article => (
        <Article key={article.id} article={article} />
      ))}
    </div>
  )
}

export default Articles
```

- Add a `components` folder in the `src`, then create `AddArticle/AddArticle.js` and `Article/Article.js`.<br>
- In the `Article.js`

```jsx
import React from "react"
import "./Article.css"

const article = ({ article }) => (
  <div className="article">
    <h1>{article.title}</h1>
    <p>{article.body}</p>
  </div>
)

export default article
```

- In the `AddArticle.js`

```jsx
import React, { useState } from "react"
import "./AddArticle.css"

const AddArticle = () => {
  const [article, setArticle] = useState()

  const handleArticleData = e => {
    setArticle({
      ...article,
      [e.target.id]: e.target.value,
    })
  }
  const addNewArticle = e => {
    e.preventDefault()
    // The logic will come later
  }

  return (
    <form onSubmit={addNewArticle} className="add-article">
      <input
        type="text"
        id="title"
        placeholder="Title"
        onChange={handleArticleData}
      />
      <input
        type="text"
        id="body"
        placeholder="Body"
        onChange={handleArticleData}
      />
      <button>Add article</button>
    </form>
  )
}
export default AddArticle
```

- In the `App.js`

```jsx
import React, { Fragment } from "react"
import Articles from "./containers/Articles"
import AddArticle from "./components/AddArticle/AddArticle";


function App() {
  return (
    <Fragment>
       <AddArticle />
       <Articles />
    </Fragment>
    )
}
export default App
```
_So, if you've done with all the instructions above, we can move on and start implementing the context API._ <br>

<div style="width:100%;height:0;padding-bottom:56%;position:relative;"><iframe src="https://giphy.com/embed/xULW8nR42RC01mpnBS" width="100%" height="100%" style="position:absolute" frameBorder="0" class="giphy-embed" allowFullScreen></iframe></div>

## Create a context
A context helps us to handle state without passing down props on every component. Only the needed component will consume the context. To implement it, we need to create (it's optional) a new folder named `context` in our project and add this code below to `aricleContext.js`.

- In `context/aricleContext.js`

```jsx
import React, { createContext, useState } from "react";

export const ArticleContext = createContext();

const ArticleProvider = ({ children }) => {
  const [articles, setArticles] = useState([
    { id: 1, title: "post 1", body: "Quisque cursus, metus vitae pharetra" },
    { id: 2, title: "post 2", body: "Quisque cursus, metus vitae pharetra" }
  ]);
  const saveArticle = article => {
    const newArticle = {
      id: Math.random(), // not really unique but it's just an example
      title: article.title,
      body: article.body
    };
    setArticles([...articles, newArticle ]);
  };
  return (
    <ArticleContext.Provider value={{ articles, saveArticle }}>
      {children}
    </ArticleContext.Provider>
  );
};

export default ArticleProvider;
```
The React library gives us access to a method called `createContext`. We can use it to as you might guess create a context. Here, we pass nothing to our context `ArticleContext`, but you can pass as argument object, array, string, etc. Then we define a function that will help us distribute the data through the `Provider`. We give to our `Provider` two values: the list of articles and the method to add an article. By the way, `articles: articles` and `saveArticle:saveArticle` is the same as `articles` and `saveArticle` it's just a convenient syntax in case it confuses you.<br>  
Now we have a context, however, we need to provide the context in order to consume it. To do that, we need to wrap our higher component with `ArticleProvider` and `App.js` might be the perfect one. So, let's add it to `App.js`.

## Provide the context
- In `App.js`

```jsx
import React from "react";

import ArticleProvider from "./context/articleContext";
import Articles from "./containers/Articles";
import AddArticle from "./components/AddArticle/AddArticle";

function App() {
  return (
    <ArticleProvider>
      <AddArticle />
      <Articles />
    </ArticleProvider>
  );
}
export default App;

```
As you see here, we first import our context provider `ArticleProvider` and wrap components that need to consume the context. Now, what about consuming the context? and how we can do that. You might be surprised how easy it is to consume the context with hooks. So, let's do that.

## Consume the context
We're going to consume the context in two components: `Articles.js` and `AddArticle.js`.
- In `Articles.js`

```jsx
import React, { useContext } from "react";

import { ArticleContext } from "../context/articleContext";
import Article from "../components/Article/Article";

const Articles = () => {
  const { articles } = useContext(ArticleContext);
  return (
    <div>
      {articles.map(article => (
        <Article key={article.id} article={article} />
      ))}
    </div>
  );
};

export default Articles;
```
With React hooks, we've now access to the `useContext` hook. And as you might guess, it will help us consume the context. By passing to our context `ArticleContext` as an argument to `useContext`, it gives us access to our state holden in `articleContext.js`. Here, we just need `articles`. Therefore, we pull it out and map through our articles and show them. Now, let's move on to `AddArticle.js`.
- In `AddArticle.js`

```jsx
import React, { useState, useContext } from "react";
import "./AddArticle.css";
import { ArticleContext } from "../../context/articleContext";

const AddArticle = () => {
  const { saveArticle } = useContext(ArticleContext);
  const [article, setArticle] = useState();

  const handleArticleData = e => {
    setArticle({
      ...article,
      [e.target.id]: e.target.value
    });
  };

  const addNewArticle = e => {
    e.preventDefault();
    saveArticle(article);
  };

  return (
    <form onSubmit={addNewArticle} className="add-article">
      <input
        type="text"
        id="title"
        placeholder="Title"
        onChange={handleArticleData}
      />
      <input
        type="text"
        id="body"
        placeholder="Body"
        onChange={handleArticleData}
      />
      <button>Add article</button>
    </form>
  );
};
export default AddArticle;
```  
As the previous case, here again we use `useContext` to pull out `saveArticle` from our context. With that, we can now safely add a new article through the React Context.<br>
We now manage our whole application's state through the React Context. However, we can still improve it through another hook named `useReducer`. <br>

<div style="width:100%;height:0;padding-bottom:79%;position:relative;"><iframe src="https://giphy.com/embed/8A7uIGtdEEPf3QvVuV" width="100%" height="100%" style="position:absolute" frameBorder="0" class="giphy-embed" allowFullScreen></iframe></div>

## Enhance the context with useReducer
The `useReducer` hook is an alternative to `useState`. It's mostly used for the more complex state. `useReducer` accepts a reducer function with the initial state of our React app, and returns the current state, then dispatches a function.<br>
This will be much more clear when we start implementing it. Now, we've to create a new file `reducer.js` in our context folder and add this code block below.
- In `reducer.js` 

```jsx
export const reducer = (state, action) => {
  switch (action.type) {
    case "ADD_ARTICLE":
      return [
        ...state,
        {
          id: Math.random(), // not really unique but it's just an example
          title: action.article.title,
          body: action.article.body
        }
      ];
    default:
      return state;
  }
};

```
As you can see, the function `reducer` receives two parameters: `state` and `action`. Then, we check if the type of action is equal to `ADD_ARTICLE` (you can create a constant or file to avoid mistyping) if it's the case add a new article to our state. This syntax might be familiar if you've used redux. Now, the logic to add a new article is handled by the reducer. We've not done yet, let's add it to our context file. 

```jsx
import React, { createContext, useReducer } from "react";
import { reducer } from "./reducer";
export const ArticleContext = createContext();

const ArticleProvider = ({ children }) => {
  const [articles, dispatch] = useReducer(reducer, [
    { id: 1, title: "post 1", body: "Quisque cursus, metus vitae pharetra" },
    { id: 2, title: "post 2", body: "Quisque cursus, metus vitae pharetra" }
  ]);

  return (
    <ArticleContext.Provider value={{ articles, dispatch }}>
      {children}
    </ArticleContext.Provider>
  );
};

export default ArticleProvider;
``` 
Here, we start by importing the `useReducer` hook and our function `reducer`. As I mentioned earlier, `useReducer` takes a function. Therefore, we've to pass our reducer function to it and as second argument the initial state of our application. Now, `useReducer` gives us access to our `articles` and a `dispatch` function (you can name it whatever you like). And we can now, update our provider with these new values given by `useReducer`.<br>
You can already see that our context file is now much cleaner. By renaming the function which adds a new article to `dispatch`, we've now to update a little bit our `AddArticle.js` file.
- In `AddArticle.js`

```jsx
import React, { useState, useContext } from "react";
import "./AddArticle.css";
import { ArticleContext } from "../../context/articleContext";

const AddArticle = () => {
  const { dispatch } = useContext(ArticleContext);
  const [article, setArticle] = useState();

  const handleArticleData = e => {
    setArticle({
      ...article,
      [e.target.id]: e.target.value
    });
  };

  const addNewArticle = e => {
    e.preventDefault();
    dispatch({ type: "ADD_ARTICLE", article });
  };

  return (
    <form onSubmit={addNewArticle} className="add-article">
      <input
        type="text"
        id="title"
        placeholder="Title"
        onChange={handleArticleData}
      />
      <input
        type="text"
        id="body"
        placeholder="Body"
        onChange={handleArticleData}
      />
      <button>Add article</button>
    </form>
  );
};
export default AddArticle;
```
Now, instead of pulling out `saveArticle`, now we get the `dispatch` function. It expects a type of action `ADD_ARTICLE` and a value `article` which will be the new article. With that, our project is now managed through the context API and React Hooks.

## Redux VS the React Context: Who wins?
You can now clearly see the difference between Redux and React Context through their implementations on our project. However, Redux is far from dead or be killed by React Context. Redux is such a boilerplate and requires a bunch of libraries. But it remains a great solution towards props drilling.<br>
The context API with hooks is much easier to implement and will not increase your bundle size.<br>
However, who wins? in my opinion, for low-frequency updates like locale, theme changes, user authentication, etc. the React Context is perfectly fine. But with a more complex state which has high-frequency updates, the React Context won't be a good solution. Because, the React Context will trigger a re-render on each update, and optimizing it manually can be really tough. And there, a solution like Redux is much easier to implement. 

You can find the finished project here {% github ibrahima92/react-context-hooks-example %}

| [TWITTER](https://twitter.com/ibrahima92_) | [BLOG](https://www.ibrahima-ndaw.com/) | [NEWSLETTER](https://ibrahima-ndaw.us5.list-manage.com/subscribe?u=8dedf5d07c7326802dd81a866&id=5d7bcd5b75) | [GITHUB](https://github.com/ibrahima92) | [LINKEDIN](https://www.linkedin.com/in/ibrahima-ndaw/) | [CODEPEN](https://codepen.io/ibrahima92) | [DEV](https://dev.to/ibrahima92) |
| -------------- | ------------------------------------------ | ----------------------------------------- | ----------------------------------------------------------------------------------------------------------- | ----------------------------- | ------------------------------------------------------ | ---------------------------------------- | -------------------------------- |