Modern React applications often need predictable, centralized state management. Redux, along with its official toolset Redux Toolkit (RTK), provides a robust Pattern for handling1900 complex global state. This Redux integration works by creating a single source of truth—a store—that React components can interact with through hooks.
Centralizing state with Redux involves several 1970 key pieces:
- Slices (redcuers + actions bundled together)
- A configured store
- React bindings via
react-reduxhooks
1. Setting Up the Project
Install the required packages:
npm install @reduxjs/toolkit react-redux
2. Defining State Logic with a Slice
A slice combines a reducer and its action creators. Create a features/tally directory and a tallySlice.js file:
// features/tally/tallySlice.js
import { createSlice } from '@reduxjs/toolkit';
const tallySlice = createSlice({
name: 'tally',
initialState: { value: 0 },
reducers: {
increased: (state) => {
state.value++;
},
decreased: (state) => {
state.value--;
},
reset: (state) => {
state.value = 0;
},
},
});
export const { increased, decreased, reset } = tallySlice.actions;
export default tallySlice.reducer;
RTK uses Immer internally, so we can write "mutative" logic like state.value++ that stays immutable under the hood.
3. Creating the Redux Store
Use configureStore to set up the store and automatically enable the Redux DevTools extension:
// app/store.js
import { configureStore } from '@reduxjs/toolkit';
import tallyReducer from '../features/tally/tallySlice';
const store = configureStore({
reducer: {
tally: tallyReducer,
},
});
export default store;
The store now holds the tally slice of state.
4. Providing the Store to React
Wrap the top-level component (typically App) with the Provider from react-redux:
// App.jsx
import React from 'react';
import { Provider } from 'react-redux';
import store from './app/store';
import TallyDisplay from './features/tally/TallyDisplay';
function App() {
return (
<Provider store={store}>
<TallyDisplay />
</Provider>
);
}
export default App;
5. Reading and Updating State from Components
Accessing state and dispatching actions is done through the useSelector and useDispatch hooks.
// features/tally/TallyDisplay.jsx
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increased, decreased, reset } from './tallySlice';
function TallyDisplay() {
const count = useSelector((state) => state.tally.value);
const dispatch = useDispatch();
const handleUp = () => dispatch(increased());
const handleDown = () => dispatch(decreased());
const handleReset = () => dispatch(reset());
return (
<div style={{ padding: '2rem', fontFamily: 'system-ui' }}>
<h2>Current Tally</h2>
<p style={{ fontSize: '3rem', margin: '1rem 0' }}>{count}</p>
<div style={{ display: 'flex', gap: '0.5rem' }}>
<button onClick={handleUp}>+</button>
<button onClick={handleDown}>-</button>
<button onClick={handleReset}>↺ Reset</button>
</div>
</div>
);
}
export default TallyDisplay;
useSelectorextractsstate.tally.value; the component re-renders whenever that value changes.useDispatchreturns the store's dispatch function;16 we dispatch action creators likeincreased()directly.
6. Handling Async Logic (Optional)
add asynchronous1010 behavior using RTK’s createAsyncThunk for side effects like API calls. For example:
import { createAsyncThunk } from '@reduxjs/toolkit';
export const fetchRemoteCount = createAsyncThunk(
'tally/remoteLoad',
async (_, thunkAPI) => {
const res = await fetch('/api/counter-value');
return res.json();
}
);
add extraReducers inside the slice to handle pending/fulfilled/rejected actions.
##encoding Structure
When the app loads:
- The store is created with
tallyReducer. Providermakes the store available.TallyDisplayreads the42 count viauseSelector.- User clicks trigger
dispatch(increased())etc. - The slice reducer updates
state.valueimmutably. - The component automatically re-renders with the new value.
This approach keeps800 global state800 decoupled from the UI, making800 behavior easier to extend, test, and debug.