Back
Tech 8 min read - 25 Feb 21 - Damien Deredec

Global data management in a React application: Redux VS Context API

Among the multiple JavaScript frameworks available in the web development ecosystem, React is one of the most widely adopted solutions today: it aims to simplify the development of visual interfaces thanks to its philosophy of using independent and reusable components.
Each component is isolated from the rest of the application and uses two types of data:
  • the props, information passed to the component by its parent component
  • the state, information managed locally by the component

1. The prop-drilling issue

When a React application starts to become more complex, it's common to see cases where a developer will pass props to a component purely for that component to transmit them to its child components.
This case generally doesn't pose a problem when it comes to passing a prop between 2 or 3 levels of components.
However, in certain cases, information may be passed through more than 5 different levels in the component tree (for example, managing authentication-related data).
This issue is called the prop-drilling, in other words, data is passed through a large number of components even though only a minority uses the transmitted information. To remedy this problem, one of the most commonly used solutions is to implement a global state – information accessible to the entire application. This can be done with solutions such as Redux or the Context API of React.

2. Redux & Context API

Redux

Redux is an external library to React that focuses on data management in JavaScript applications. It's important to note that Redux can also be used with other web applications built without React, but it's regularly associated with this framework.
Redux provides a store, a JavaScript object that contains various globally stored information. The library react-redux then provides two very practical hooks: useSelector and useDispatch, which allow us to retrieve globally stored data, but also to trigger actions that enable modifying this state global.
Prerequisites npm install --save redux react-redux
These 2 dependencies must be installed to start using Redux in a React application.
Setting up the Redux store language=js import { createStore } from "redux"; import { Provider } from "react-redux"; import { render } from "react-dom"; import reducer from "./reducer"; const store = createStore(reducer); const App = ()  => ( <Provider store={store}>     <Page />   </Provider> ); render(<App />, document.getElementById("root"));
This first part of the code allows us to set up the store made available by Redux. It corresponds to the object that will contain the "global" data shared by the rest of the application.
Setting up Redux's "reducer" language=js const initialState = { isLogged: false }; export default function reducer(state = initialState, action) { switch (action.type) { case "SET_IS_LOGGED": return { ...state, isLogged: action.payload }; default: return state; } };
This function corresponds to the reducer used by Redux. In this function, we define the global data and the various actions that allow for modifying the global data of the store.
A reducer receives 2 parameters: the state global data currently stored in Redux (where we specify the state initial) and then the actions which will be received by the reducer. An action corresponds to a 'request' to modify the state global data; it is represented by a JavaScript object containing at least a 'type' key.
The type of the action contains a unique character string that will be used to identify which data to modify in the store of Redux.
In the code snippet above, we use a switch allowing to alternate the behaviour of the reducer depending on the action type.
Retrieval of global data and data modifications language=js import React from "react"; import { useSelector, useDispatch } from "react-redux"; export default function DeeplyNestedComponent() {   const globalState = useSelector((state) => state);   const dispatch = useDispatch();   return (     <div>       {globalState.isLogged ? (         <button onClick={() => dispatch({ type: "SET_IS_LOGGED", payload: false })}> Log out         </button> ) : (         <button onClick={() => dispatch({ type: "SET_IS_LOGGED", payload: true })}> Log in </button>       )} </div> ); }
By using the hooks useSelector and useDispatch, we can retrieve globally stored information and dynamically change the display of authentication buttons. By 'dispatching an action', this allows us (in our example) to log in or log out.
  • useSelector : Hook for retrieving globally stored data, from any component
  • useDispatch : Hook for retrieving a "dispatch" function that allows sending actions (as objects) which will be processed by the reducer set up above
You can go even further with Redux by following the documentation available on their website.

Context API

Officially launched in March 2018, the Context API of React allows for storing information in a 'context'. The context provides a provider (a React component) that allows all child components to access the information stored in the context. Its usage has notably been simplified with the arrival of the hook useContext made natively available in React.
Context Creation language=js import React, { useState, createContext } from "react"; const AuthContext = createContext(); const AuthProvider = (props) => { const [authState, setAuthState] = useState({ isLogged: false });   return (     <AuthContext.Provider value={[authState, setAuthState]}>       {props.children} </AuthContext.Provider> ); }; export { AuthContext, AuthProvider };
Like Redux, the Context API provides a provider, the component responsible for passing globally stored information. However, unlike the concept of dispatch and actions of Redux, here we transmit via the context a state and a setState. In this context, we can easily retrieve and modify globally stored information, as if we were doing it in a local component. The Context API does not restrict the type of information that can be transferred. You can perfectly well provide a value (a string of characters, a number, a date, ...), or an object or an array of elements.
Context Setup language=js import React from "react"; import { AuthProvider } from "./contexts/auth-context"; import AppRouter from "./navigation/app-router"; export default function App() {   return (     <div>       <AuthProvider>         <AppRouter />       </AuthProvider>     </div>   ); }
As in the case of Redux, we place the provider at the root of the application so that the information is available everywhere. It is interesting to note that this decision is purely arbitrary: the store of Redux is generally always placed at the root of the application, whereas a context can very well be positioned deeper in the component tree of the application; for example, it could be applied to a specific screen.
Retrieval and modification of global context data language=js import React, { useContext } from "react"; import { AuthContext } from "./context"; function AuthButtons() {   const [authState, setAuthState] = useContext(AuthContext);   return (     <div>       {authState.isLogged ? (         <button onClick={setAuthState((state) => ({ ...state, isLogged: false }))}>           Log out         </button>       ) : (         <button onClick={setAuthState((state) => ({ ...state, isLogged: true }))}>           Log in         </button>       )}     </div>   ); } export default AuthButtons;
The use of the hook useContext allows us to retrieve our authState and our setAuthState that we passed in the 'value' parameter of our provider.
For more information on the Context API of React, you can refer to its documentation.

3. Comparison

Redux and the Context API are two viable solutions for making a state global. We will now list the pros and cons of each solution to help you select the best option.

Redux

Advantages :
  • Architecture of responsibilities (reducers, actions, …) well-defined and constant
  • Browser extensions like "Redux DevTools" are available and allow easy debugging of Redux integration in a React project.
Disadvantages :
  • Difficult to adopt when starting with React
  • External library to React: the two necessary packages (redux & react-redux) represent additional data to download for the end-user

Context API

Advantages :
  • Native solution to the React library, it is available as soon as you start a new React application
  • Very easy to get started with
  • It is possible to integrate more "local" contexts, i.e., which do not encompass the entire application but rather a small number of components: this allows for segmenting the application's logic.
Disadvantages :
  • The Context API is not suitable for frequent changes to the state (for example, for the value of a text field)
  • All components consuming the context will be re-render on each change of the global state provided by the context, even if the information they use is not changed.

Conclusion

Redux is a library used since 2015; it is present in a very large number of React applications in production. It has been tested and approved many times and will remain a particularly interesting option when a React application starts to grow in scale and have a substantial codebase.
However, if you wish to integrate some global data quickly and in a more simplified manner, the Context API seems capable of meeting your expectations. It is directly integrated into React.
Neither of these two options is predominant. Depending on the scope of your project and the implementation you wish to achieve, one of these two solutions will allow you to manage your application's global data.
To go further, I offer the following links which provide additional information that could guide your choice more precisely:

Do you want support to launch your digital project?

Submit your project now