When it comes to managing state in large React applications, two approaches emerge as the most popular: Redux and React's Context API. Both have their advantages and disadvantages, but what if you want to combine them to get the best of both worlds? In this article, we'll examine how to achieve this integration and what benefits it can bring to your project.
Why consider the combination?
Managing state in complex applications can be a real challenge. Redux, with its state-centralized architecture, makes it possible to manage changes in a predictable and consistent manner. However, it's not uncommon for its boilerplate to become a burden. On the other hand, the Context API is native to React and offers an easy way to share data between components without having to manually pass props, but it has limitations when more structured state management is required.
We integrated these approaches not only to improve the efficiency of state management, but also to reduce the amount of redundant code that often accompanies Redux.
Setting up the project
We'll start by setting up a new React project in which we'll implement both Redux and the Context API. First, make sure you have Node.js and a package manager like npm or yarn installed. Initialize a new project using Create React App:
$ npx create-react-app my-redux-context-app
$ cd my-redux-context-app
Installing Redux and initial configurations
Once the project is created, proceed to install Redux and react-redux using npm or yarn:
$ npm install redux react-redux
Create a folder called store inside the src directory and inside it add a file called index.js. This is where you’ll define your reducers and create the store:
// src/store/index.js
import { createStore } from redux;
// Define your reducer
const initialState = {};
function rootReducer(state = initialState, action) {
return state;
}
const store = createStore(rootReducer);
export default store;
Context API Implementation
Now we move on to the Context API. Create a new folder called context inside the src directory. Inside, create an AppContext.js file:
// src/context/AppContext.js
import React from react;
const AppContext = React.createContext();
export default AppContext;
Next, we wrap our application with both the Redux Provider and the Context Provider:
// src/index.js
import React from react;
import ReactDOM from react-dom;
import { Provider } from react-redux;
import App from ./App;
import store from ./store;
import AppContext from ./context/AppContext;
ReactDOM.render( , document.getElementById(root));
Practical case: Combined counter
Let's imagine we want to implement a counter whose state depends on both our global store via Redux and the local context. We will use Redux to handle the actions related to increment or decrement, while we will define some specific value in our context.
// src/components/Counter.js
import React from react;
import { useSelector, useDispatch } from react-redux;
import AppContext from ../context/AppContext;
function Counter() {
const count = useSelector((state) => state.count);
const dispatch = useDispatch();
const contextValue = React.useContext(AppContext); // Getting data from the context {count}</p>
<p>Value from context: {contextValue.someValue}</p>
</div>
}
export default Counter;
Advantages and Limitations
The main advantage of using both systems is the flexibility in terms of state control. It allows you to segment responsibilities and optimize specific parts according to the needs of the component or module in question. However, integrating dissimilar systems like these can increase overall code complexity if not managed properly.