React performance optimization and Redux toolkit implementation integration

Mobile Developer
July 8, 2024
Updated on February 12, 2025
0 MIN READ
#typescript#next-js#react#performance

React Performance Optimization and Redux Toolkit Implementation Integration

Introduction

React is a powerful library for building user interfaces, but as applications grow in complexity, performance bottlenecks can emerge. Combining React with Redux for state management is a common pattern, but without proper optimization, it can lead to unnecessary re-renders and sluggish performance.

In this post, we’ll explore key React performance optimization techniques and how to integrate Redux Toolkit—a modern, opinionated way to use Redux—to streamline state management while maintaining high performance.


1. React Performance Optimization Techniques

Memoization with React.memo and useMemo

React re-renders components when their props or state change. For functional components, React.memo prevents unnecessary re-renders by memoizing the component. Similarly, useMemo caches expensive computations.

const ExpensiveComponent = React.memo(({ data }) => { const processedData = useMemo(() => expensiveCalculation(data), [data]); return <div>{processedData}</div>; });

Avoiding Inline Functions and Objects in Props

Inline functions and objects in props cause unnecessary re-renders because they create new references on every render. Instead, memoize them with useCallback and useMemo.

const handleClick = useCallback(() => { console.log('Button clicked'); }, []); const config = useMemo(() => ({ color: 'blue', size: 'large' }), []); return <Button onClick={handleClick} style={config} />;

Virtualization for Large Lists

Rendering large lists can degrade performance. Libraries like react-window or react-virtualized render only visible items, reducing DOM nodes and improving speed.

import { FixedSizeList as List } from 'react-window'; const Row = ({ index, style }) => ( <div style={style}>Row {index}</div> ); const BigList = () => ( <List height={600} itemCount={1000} itemSize={35} width={300}> {Row} </List> );

2. Redux Toolkit: A Modern Approach to State Management

Redux Toolkit (RTK) simplifies Redux setup by reducing boilerplate and providing built-in best practices like Immer for immutable updates and createSlice for reducers.

Setting Up Redux Toolkit

Install Redux Toolkit:

npm install @reduxjs/toolkit react-redux

Creating a Slice

A slice automatically generates actions and reducers:

import { createSlice } from '@reduxjs/toolkit'; const counterSlice = createSlice({ name: 'counter', initialState: { value: 0 }, reducers: { increment: (state) => { state.value += 1; }, decrement: (state) => { state.value -= 1; }, }, }); export const { increment, decrement } = counterSlice.actions; export default counterSlice.reducer;

Configuring the Store

RTK’s configureStore sets up the Redux store with middleware (like Redux Thunk) and DevTools automatically:

import { configureStore } from '@reduxjs/toolkit'; import counterReducer from './counterSlice'; const store = configureStore({ reducer: { counter: counterReducer, }, }); export default store;

3. Optimizing Redux Performance

Selectors with createSelector

Reselect’s createSelector (included in RTK) memoizes derived data to prevent recalculations:

import { createSelector } from '@reduxjs/toolkit'; const selectTodos = (state) => state.todos; const selectCompletedTodos = createSelector( [selectTodos], (todos) => todos.filter(todo => todo.completed) );

Avoiding Unnecessary Redux Re-renders

Use useSelector wisely—components re-render only if the selected state changes. For complex state, return primitives or memoized selectors.

const count = useSelector((state) => state.counter.value); // Better than selecting the entire counter object

Normalized State Shape

Storing data in a normalized form (e.g., { ids: [], entities: {} }) reduces duplication and improves lookup efficiency. RTK’s createEntityAdapter helps with this:

import { createEntityAdapter } from '@reduxjs/toolkit'; const todosAdapter = createEntityAdapter(); const initialState = todosAdapter.getInitialState(); const todosSlice = createSlice({ name: 'todos', initialState, reducers: { addTodo: todosAdapter.addOne, updateTodo: todosAdapter.updateOne, }, });

4. Integrating React Optimization with Redux Toolkit

Combining React.memo and Redux

Wrap Redux-connected components in React.memo to prevent re-renders when props don’t change:

const TodoItem = React.memo(({ todo }) => { return <li>{todo.text}</li>; }); const mapStateToProps = (state, ownProps) => ({ todo: state.todos.entities[ownProps.id], }); export default connect(mapStateToProps)(TodoItem);

Using RTK Query for Data Fetching

RTK Query eliminates manual data-fetching logic and reduces re-renders by caching API responses:

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; const api = createApi({ reducerPath: 'api', baseQuery: fetchBaseQuery({ baseUrl: '/api' }), endpoints: (builder) => ({ getPosts: builder.query({ query: () => 'posts' }), }), }); export const { useGetPostsQuery } = api;

Conclusion

Optimizing React performance requires a mix of techniques—memoization, virtualization, and careful state management. Redux Toolkit streamlines Redux usage while promoting performance best practices like normalized state and memoized selectors.

By combining React’s optimization tools (React.memo, useMemo, useCallback) with Redux Toolkit’s efficient patterns (createSlice, createSelector, RTK Query), you can build scalable, high-performance applications without sacrificing developer experience.

Start applying these strategies today to keep your React-Redux apps fast and maintainable!

Share this article