Redux - React Example
Redux - React Example with Redux Toolkit
Section titled “Redux - React Example with Redux Toolkit”Let’s explore a simple example of integrating React with Redux using modern best practices, specifically Redux Toolkit and React Redux Hooks. We’ll build a basic counter application.
First, we set up our Redux store using configureStore from Redux Toolkit. This function simplifies store setup, automatically includes Redux Thunk middleware, and enables the Redux DevTools Extension.
import { configureStore } from '@reduxjs/toolkit';import counterReducer from '../features/counter/counterSlice';
export const store = configureStore({ reducer: { counter: counterReducer, },});
// Optional: Define RootState and AppDispatch types for TypeScript integration// export type RootState = ReturnType<typeof store.getState>;// export type AppDispatch = typeof store.dispatch;Next, the root file (index.js or main.jsx) renders the React application and wraps it with the <Provider> component from react-redux. The Provider makes the Redux store available to all components in the application.
// src/index.js (or main.jsx)
import React from 'react';import ReactDOM from 'react-dom/client';import { Provider } from 'react-redux';import { store } from './app/store';import App from './App';import './index.css';
const root = ReactDOM.createRoot(document.getElementById('root'));root.render( <React.StrictMode> <Provider store={store}> <App /> </Provider> </React.StrictMode>);Our root App component simply renders the Counter component.
import React from 'react';import Counter from './features/counter/Counter';import './App.css';
function App() { return ( <div className="App"> <header className="App-header"> <Counter /> </header> </div> );}
export default App;The Counter component uses React Redux hooks to interact with the store. useSelector reads data from the store, and useDispatch gets the dispatch function to dispatch actions.
// src/features/counter/Counter.js
import React from 'react';import { useSelector, useDispatch } from 'react-redux';import { increment, decrement, reset } from './counterSlice'; // Import actions from the slice
function Counter() { // Read the counter value from the Redux store state const count = useSelector((state) => state.counter.value); // Get the dispatch function const dispatch = useDispatch();
return ( <div> <div> <h2>Counter Value: {count}</h2> </div> <div> <button onClick={() => dispatch(increment())}> INCREMENT BY 1 </button> </div> <div> <button onClick={() => dispatch(decrement())}> DECREMENT BY 1 </button> </div> <button onClick={() => dispatch(reset())}> RESET </button> </div> );}
export default Counter;Redux Toolkit’s createSlice function simplifies creating reducers and actions. It generates action creators and action types automatically and uses Immer internally to allow ‘mutating’ logic in reducers, which it converts to safe immutable updates.
// src/features/counter/counterSlice.js
import { createSlice } from '@reduxjs/toolkit';
const initialState = { value: 0,};
export const counterSlice = createSlice({ name: 'counter', // Name used in action types initialState, // The `reducers` field lets us define reducers and generate associated actions reducers: { increment: (state) => { // Redux Toolkit allows us to write "mutating" logic in reducers. // It doesn't actually mutate the state because it uses the Immer library, // which detects changes to a "draft state" and produces a brand new // immutable state based off those changes state.value += 1; }, decrement: (state) => { state.value -= 1; }, reset: (state) => { state.value = 0; }, // Example with payload: incrementByAmount: (state, action) => { // state.value += action.payload; // }, },});
// Action creators are generated for each case reducer functionexport const { increment, decrement, reset } = counterSlice.actions;
// The function below is called a selector and allows us to select a value from// the state. Selectors can also be defined inline where they're used instead of// in the slice file. For example: `useSelector((state) => state.counter.value)`// export const selectCount = (state) => state.counter.value;
// Export the reducer function generated by createSliceexport default counterSlice.reducer;Initially, the app displays the counter with a value of 0.
When you click ‘INCREMENT BY 1’ twice, the displayed value becomes 2.
Clicking ‘DECREMENT BY 1’ once will change the value to 1.
Clicking ‘RESET’ will return the counter value to 0.
Using the Redux DevTools browser extension, you can inspect the actions being dispatched and the resulting state changes. For example, after the first increment, you would see an action with type ‘counter/increment’ and the state changing from { value: 0 } to { value: 1 }. The DevTools allow time-travel debugging, inspecting action payloads, and viewing the state diff.
We encourage you to try building a small Todo App using Redux Toolkit and React Hooks as an exercise to further solidify your understanding.
For more details, refer to the official documentation:
- Redux Toolkit: https://redux-toolkit.js.org/
- React Redux: https://react-redux.js.org/