Redux is a state management library for JavaScript applications, particularly popular with React. It provides a predictable and centralized way to manage application state, making it easier to handle complex data flows and state changes in large applications.
Key concepts of Redux include:
- Store: A single source of truth that holds the entire application state
- Actions: Plain objects describing what happened in the application
- Reducers: Pure functions that specify how the application’s state changes in response to actions
Setting up Redux Toolkit in a React TypeScript project involves several steps. Let’s go through them one by one.
1. Installation
First, install the required dependencies:
# NPM
npm install @reduxjs/toolkit react-redux --legacy-peer-deps
2. Create Store Configuration
Create a new file called store.ts
in your src
directory:
import { configureStore } from "@reduxjs/toolkit";
export const store = configureStore({
reducer: {
// Add your reducers here
},
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
3. Create Custom Hooks
Create a new file called hooks.ts
to define typed hooks:
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import type { RootState, AppDispatch } from "./store";
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
4. Create a Slice
Create a slice for your feature (e.g., counterSlice.ts
):
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
interface CounterState {
value: number;
}
const initialState: CounterState = {
value: 0,
};
export const counterSlice = createSlice({
name: "counter",
initialState,
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action: PayloadAction<number>) => {
state.value += action.payload;
},
},
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;
5. Add Reducer to Store
Update your store.ts
to include the new reducer:
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./counterSlice";
export const store = configureStore({
reducer: {
counter: counterReducer,
},
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
6. Wrap Your App with Provider
In your index.tsx
or App.tsx
:
import React from "react";
import { Provider } from "react-redux";
import { store } from "./store";
import App from "./App";
const Root = () => {
return (
<Provider store={store} children={undefined}>
<App />
</Provider>
);
};
export default Root;
7. Use Redux in Components
Example of using Redux in a component:
import React from "react";
import { useAppSelector, useAppDispatch } from "./hooks";
import { increment, decrement } from "./counterSlice";
const Counter = () => {
const count = useAppSelector((state) => state.counter.value);
const dispatch = useAppDispatch();
return (
<div>
<div>Count: {count}</div>
<button onClick={() => dispatch(increment())}>Increment</button>
<button onClick={() => dispatch(decrement())}>Decrement</button>
</div>
);
};
export default Counter;
Best Practices
- Always use TypeScript types for better type safety
- Use the custom hooks (
useAppDispatch
anduseAppSelector
) instead of plain Redux hooks - Keep your slices organized and modular
- Use meaningful names for your actions and reducers
- Consider implementing error handling in your reducers