When it comes to React, managing state is an essential concept helping us synchronize the state of React Apps throughout all components. The simplest way to pass data from one component to another is to put it directly to the children that require it as a prop. But how can we take the data from a deep component or repeat it in many different parts? Here is when Redux and React Context come into play. Although both of them are used to handle data in React, there are still some things that make them distinctive.

What is State Management?

Since the state in React Apps can be represented in any data type as well as it can be allocated throughout components, we need to manage the state. In simple words, state management is a logic of storing and keeping track of data displayed on the front-end. For instance, it can tell us if the theme is set to dark or light, the button is switched on or off, and so on. In conclusion, state management is a great way to synchronize the data across all elements and communicate with the backend.

Managing state with React Context and Redux

React Context

With the arrival of React v16.3, React Context was introduced as a way to store data and share them among components. But now, let's go through a real example called Counter. The Counter has an initial value of 0 and has two buttons to increase and decrease its value.

// counterReducer.js
 
export default counterReducer = (state, action) => {
 switch(action.type) {
   case "INCREASE_COUNTER":
     return {
       ...state,
       counter: state.counter + 1
     }
 
   case "DECREASE_COUNTER":
     return {
       ...state,
       counter: state.counter - 1
     }
 
   default:
     return state;
 }
}

/ Context API index.js
 
import React, { createContext, useReducer } from "react";
import counterReducer from '../counterReducer';
 
const initialState = {
 counter: 0
}
export const GlobalContext = React.createContext(initialState);
export function CounterProvider({ children }) {
 const [state, dispatch] = useReducer(counterReducer, initialState);
 
 return (
   <GlobalContext.Provider
     value={{
       state,
       dispatch
     }}
   >
     {children}
   </GlobalContext.Provider>
);
}
 
export const useCounterContext = () => React.useContext(GlobalContext);

…and import it into “App.js”

import React from "react";
import { GlobalContext } from "./globalState";
import { Main } from "./Main";
 
export const App = () => {
 return (
   <GlobalContext>
     <Main />
   </GlobalContext>
 )
}

To use it inside the component, we just need to import useCounterContext from “globalState”.

// Main.js
 
import { useCounterContext } from "./globalState";
 
export const Main = () => {
 const { state, dispatch } = useCounterContext();
 
 return (
   <div>
     <p>Counter : {state}</p>
     <button
       onClick={() => dispatch({
         type: "INCREASE_COUNTER"
       })}
     >
       Increase
     </button>
     <button
       onClick={() => dispatch({
         type: "DECREASE_COUNTER"
       })}
     >
       Decrease
     </button>
   </div>
 )
};
 
 

That’s all! So easy for everyone to understand.

Redux

Redux, for years, has always been known as the most popular way for state management, but it is much more complicated than React Context. Thus, there will be a lot of obstacles for beginners who are just starting to learn and work with React.

To know what it really is and how to implement it let’s go to the same example as above with Counter. Remember that Redux requires three different blocks to function, including Actions, Reducers, and Store. Below are examples of them:

// action.js
 
export const increase = () => {
 return {
   type: "INCREASE_COUNTER",
 }
};
 
export const decrease = () => {
 return {
   type: "DECREASE_COUNTER",
 }
}
 
// reducer.js
 
let initialState = {
 counter: 0,
}
 
export default counterReducer = (state = initialState, action) => {
 switch(action.type) {
   case "INCREASE_COUNTER":
     return {
       ...state,
       counter: state.counter + 1,
     }
 
   case "DECREASE_COUNTER":
     return {
       ...state,
       counter: state.counter - 1,
     }
 
   default:
     return state
 }
}
// store.js
 
import { createStore } from "redux";
import counterReducer from "./reducer";
 
export const store = createStore(counterReducer);

Next, to make the Store accessible in our application and its children components, we also need to import it into “App.js”

// App.js
 
import { Provider } from "react-redux";
import { Main } from "./Main";
import { store } from "./store";
 
export default App = () => {
 return (
   <Provider store={store}>
     <Main />
   </Provider>
 )
}

After that, to access the state and make a change, we can use useSelector() and useDispatch() hooks by importing them from “react-redux”:

// Main.js
 
import { useSelector, useDispatch } from "react-redux";
import { increase, decrease } from "./action";
 
export const Main = () => {
 const counter = useSelector(state => state.counterReducer.counter);
 const dispatch = useDispatch();
 
 return (
   <div>
     <p>Counter : {counter}</p>
     <button
       onClick={() => dispatch(increase())}
     >
       Increase
     </button>
     <button
       onClick={() => dispatch(decrease())}
     >
       Decrease
     </button>
   </div>
 )
};

There are a lot of things that need to be done, and, as you can see, using Redux is tougher than using React Context.

The differences between React Context and Redux

After going through all examples, it’s time for us to make a comparison between them:

React Context

Redux

The Built-in tool that is already available in React

A state management library

Requiring to create new files when adding new contexts

Easy to extend since the ease of adding new data after initial setup

Hard to debug

Have powerful Redux DevTools for debugging purposes 

Easier to understand and handle for beginners

May be misleading for beginners, even with Redux DevTools

State is changeable 

The state is read-only and can not update directly

Better to use with small applications

Larger applications are more suitable

Conclusion

After all, you may wonder which one is better for React Apps. To be honest, this is a confusing question. It depends on your app’s size, how often the data needs to be refreshed, and so on. Remember that changing between Redux and React Context could be hard and time-consuming, so take a look at the comparison table and make a decision at the beginning of your work.