Supercharge Your React App with Redux Toolkit Integration
Ibrahim Hz
Posted on December 1, 2023
Learn Redux toolkit integration in React for state management. Covers store, actions, reducers, selectors. Plus configuring store with React Redux and RTK.
Introduction
React is a popular JavaScript library for building user interfaces. It allows you to build composable components and manage application state efficiently. However, as your React app grows, managing state can become difficult.
This is where Redux comes in. Redux is a predictable state container for JavaScript apps. It helps you manage application state outside of your React components in a single store. This makes it easier to track state changes, debug issues, and build complex logic.
Redux Toolkit is the official Redux wrapper that streamlines configuring and setting up Redux. It provides useful utilities like createSlice for writing reducer logic and configureStore for setting up the Redux store. Integrating Redux Toolkit into your React app makes it easier to start using Redux with good defaults and best practices built-in.
The key benefits of integrating Redux Toolkit into a React app include:
- Simplified state management
- Powerful slicing and action generation
- Simpler async logic handling
- Better development experience with devtools
- Improved performance with optimizations built-in
In this article, we’ll walk through a project to demonstrate how to integrate Redux Toolkit into a React app. We’ll set up the store, connect it to components, write slice reducers, perform async logic, and more. By the end, you’ll have a supercharged React app leveraging the power of Redux Toolkit for robust state management.
Project Setup
To get started, we need to create a new React app and install Redux Toolkit.
First, create a new React app using Create React App:
npm create vite@latest my-vue-app
cd my-app
Next, install Redux Toolkit and React Redux:
npm install @reduxjs/toolkit react-redux
Redux Toolkit includes the core Redux library, so there is no need to install Redux separately.
That covers the basics for setting up a new React Redux app! We have a barebones React app generated with Create React App, and Redux Toolkit + React Redux installed.
Now we can move on to configuring the Redux store and integrating it with React.
Store Setup
The first step in integrating Redux Toolkit is to configure the Redux store. This is where we will add the slice reducers and middleware.
Redux Toolkit provides a configureStore()
method that makes setting up the store simple. To use it, we need to import the configureStore method and the slicer reducers we want to include:
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from '../features/counter/counterSlice'
import postsReducer from '../features/posts/postsSlice'
Then we call configureStore()
and pass in an object with any middleware we want to use and the reducer parameter where we add the slice reducers:
const store = configureStore({
reducer: {
counter: counterReducer,
posts: postsReducer
}
})
The keys we give the reducers will be used as the state key names later on.
Now we have a configured Redux store with the slice reducers added! The slices handle each specific domain of data and state logic, while the store brings it all together into one state object.
Next we need to provide the React app access to the store.
Provider Setup
The Redux store needs to be made available to the entire React component tree. This is done by wrapping the App component inside the <Provider>
component exported by React Redux.
The component accepts the Redux store as a prop and makes it available down the component tree via the useSelector hook or connect() function. This allows any component in the application access to the Redux store and state.
To set up the provider, first import it from ‘react-redux’
:
import { Provider } from 'react-redux';
Then wrap the <App>
component with the provider component, passing in the store:
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
Now any component in the application can access the Redux store created earlier. This gives us the power to connect any component we want to Redux simply by using React Redux hooks like useSelector or useDispatch.
The provider component is what wires up the Redux store to make it available to all our components. This crucial setup allows the entire React app access to Redux and enables powerful state management across the application.
Create Slice Reducers
Redux Toolkit allows us to write “slice reducers” that contain the reducers, actions, and action creators needed to manage data for a specific feature or domain.
To create a slice reducer, we can use the createSlice function from Redux Toolkit.
import { createSlice } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: {
value: 0
},
reducers: {
increment: state => {
state.value += 1;
},
decrement: state => {
state.value -= 1;
}
}
});
This createSlice call takes an object with a name field, an initial state, and a reducers object that contains Redux action definitions as functions.
Redux Toolkit will automatically generate action creator functions corresponding to each reducer function we define. For example, the increment and decrement functions will generate increment and decrement action creators.
We can export the generated actions and the reducer function as named exports:
export const { increment, decrement } = counterSlice.actions;
export default counterSlice.reducer;
Now we can import these slices into our store setup and use the action creators and reducers that were generated for us.
This allows us to simplify Redux code by defining each slice of state in one place, alongside the actions that can update that state.
Use Slice Reducers
When we have created our slice reducers and set up the store
and provider, we can start using those reducers in our React components. This allows us to dispatch actions and select state in our components.
To dispatch actions, we first need to import the action creators from the slice files:
import {fetchUsers} from './features/usersSlice'
Then we can dispatch the actions in component code, such as in a useEffect
hook:
useEffect(() => {
dispatch(fetchUsers())
}, [])
This will dispatch the fetchUsers action to the store.
To select state, we use the useSelector
hook from React Redux. For example:
const users = useSelector(state => state.users.data)
This allows us to extract the users data from the state into a variable we can use in our component.
The useSelector hook will subscribe to the Redux store, so whenever the state updates from a dispatched action, it will automatically re-run the selector and update the component with the new data.
This pattern allows us to keep our application logic centralized in the Redux store, while the components simply fetch data from the store and render the UI. This separation of concerns helps manage complexity as the app grows.
Async Logic
Redux Toolkit allows us to easily integrate async logic like data fetching into our Redux store. This is done through thunks, which are functions that can contain asynchronous logic inside.
Adding Async Thunks
We can create thunks using Redux Toolkit’s createAsyncThunk
API. This takes in the thunk name and an async function that returns a promise:
const fetchUserById = createAsyncThunk(
'users/fetchById',
async (userId) => {
const response = await fetchUserData(userId);
return response.data;
}
);
The thunk will automatically dispatch pending, fulfilled, and rejected actions based on the promise status.
Dispatching Thunks
We can dispatch the thunks like regular Redux actions. This will trigger the async logic:
dispatch(fetchUserById(1));
The component can use the custom hook useSelector to get the async data from the Redux state once resolved.
This allows us to keep complex async logic outside our components, making them more focused and testable. Redux Toolkit thunks provide a powerful and simple abstraction for async data fetching.
Performance
When building large React apps, performance optimization becomes critical. Two key techniques for improving performance in React Redux apps are React.memo and React.useCallback.
React.memo is a higher order component that memoizes functional components, preventing unnecessary re-renders when the props haven't changed. To use it, simply wrap your functional component:
const MyComponent = React.memo(function MyComponent(props) {
/* only rerenders if props change */
});
React.useCallback hooks can be used to wrap functions inside components to prevent unnecessary re-creations. For example:
const increment = React.useCallback(
() => dispatch({type: 'increment'}),
[dispatch]
);
The dispatch function will only be recreated when the dispatch dependency changes.
By judiciously using React.memo and React.useCallback in performance critical spots, we can optimize our Redux apps to avoid unnecessary render cycles. The key is to identify components that re-render often with the same props, and wrap their definitions in React.memo. Also identify functions that are recreated too often, and wrap them in useCallback hooks.
With these simple but powerful tools, we can supercharge our React Redux apps!
Devtools
Redux Toolkit comes with Redux Devtools integration out of the box. Devtools allow you to inspect the state and actions of your Redux store. They are invaluable for debugging and optimizing your application.
Installing Redux Devtools
To install Redux Devtools in your React app:
- Install the devtools npm package:
npm install --save-dev @redux-devtools/extension
- Import the devtools enhancer and apply it when creating your store:
import { devToolsEnhancer } from '@redux-devtools/extension'
const store = createStore(rootReducer, devToolsEnhancer())
Using Redux Devtools
With devtools installed, open your application and look for the Redux tab in your browser’s devtools panel.
Here you can:
- View the state history and travel back and forth between states
- Dispatch actions directly to test state changes
- Export and import state snapshots to persist or share
- Monitor performance and detect unnecessary re-renders
- Filter actions to focus on specific events
- And much more!
Redux Devtools provide powerful capabilities to understand exactly what is happening in your app. Take advantage of them to build robust Redux applications.
Conclusion
Integrating Redux Toolkit into your React application can help manage state across components more efficiently. Here’s a summary of the main points we covered:
- Set up Redux store with configureStore and initial state
- Create slice reducers with createSlice to define state mutations
- Connect components to store with and useSelector/useDispatch hooks
- Dispatch actions to reducers to update state
- Add async thunks for async logic like data fetching
- Use configureStore optimizations like reducer memoization
- Debug with Redux DevTools for state monitoring
For more information on leveraging Redux Toolkit with React, check out these additional resources:
Redux Toolkit Docs — Official docs with API references
React Redux Docs — Using Redux with React
With Redux Toolkit and React, you can build powerful applications with clean, scalable state management. The integration helps centralize state, while still keeping React fast and reactive.
That’s it for this article! I hope you enjoyed it. Follow me for more full stack devlopment blog post and comment for any feedback you might have about this article.
I‘m Mohammed Ibrahim aka ibrahimhz. You can find me on LinkedIn or maybe follow me on GitHub as well.
Posted on December 1, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.