createSliceis a function in Redux Toolkit that simplifies the process of creating Redux reducers, actions, and initial state. It drastically reduces boilerplate code compared to writing them manually.
Core Concepts
- Reducer Logic:
createSliceencapsulates the logic for updating your state. - Action Creators:It automatically generates action creators based on the reducers you define.
- Initial State:You provide the initial state for your slice.
- Immutability:Redux Toolkit leverages Immer internally, allowing you to write “mutating” logic within your reducers, which Immer then converts into immutable updates. This makes state updates much easier to reason about.
Basic Structure
import { createSlice } from '@reduxjs/toolkit';
const mySlice = createSlice({
name: 'mySlice', // A unique name for your slice
initialState: {
// Your initial state object
counter: 0,
message: 'Initial Message'
},
reducers: {
// Reducers are functions that modify the state
increment: (state) => {
state.counter += 1;
},
decrement: (state) => {
state.counter -= 1;
},
setMessage: (state, action) => {
state.message = action.payload;
},
reset: (state) => {
state.counter = 0;
state.message = 'Reset Message';
}
}
});
// Export the actions and reducer
export const { increment, decrement, setMessage, reset } = mySlice.actions;
export default mySlice.reducer;
Explanation
name: A string that uniquely identifies this slice. This is used as a prefix for the generated action types.initialState: An object representing the initial state of this slice of the Redux store.reducers: An object where each key is an action type (e.g.,increment,decrement) and each value is a reducer function.- Reducer Functions: These functions take the current
stateand anactionas arguments. They mutate thestatedirectly (thanks to Immer), and Redux Toolkit handles creating the immutable updates. action.payload: Theactionobject often contains apayloadproperty, which holds any data you want to pass to the reducer.
- Reducer Functions: These functions take the current
Using the Slice
Configure the Store: Import the reducer into your Redux store configuration.
import { configureStore } from '@reduxjs/toolkit'; import mySliceReducer from './mySlice'; const store = configureStore({ reducer: { mySlice: mySliceReducer, }, }); export default store;Dispatch Actions: Use the generated action creators to dispatch actions.
import { useDispatch } from 'react-redux'; import { increment, decrement, setMessage } from './mySlice'; function MyComponent() { const dispatch = useDispatch(); return ( <div> <button onClick={() => dispatch(increment())}>Increment</button> <button onClick={() => dispatch(decrement())}>Decrement</button> <button onClick={() => dispatch(setMessage('Hello from component!'))}>Set Message</button> </div> ); }Select State: Use
useSelectorto access the state.import { useSelector } from 'react-redux'; function MyComponent() { const counter = useSelector((state) => state.mySlice.counter); const message = useSelector((state) => state.mySlice.message); return ( <div> <p>Counter: {counter}</p> <p>Message: {message}</p> </div> ); }
Extra Reducers (Handling Asynchronous Actions)
createSlice also allows you to define extraReducers to handle actions that are not generated by the slice itself (e.g., actions dispatched from thunks or sagas).
import { createSlice } from '@reduxjs/toolkit';
const mySlice = createSlice({
name: 'mySlice',
initialState: {
data: null,
loading: false,
error: null
},
reducers: {
// ... your regular reducers
},
extraReducers: (builder) => {
builder
.addCase('fetchData/pending', (state) => {
state.loading = true;
state.error = null;
})
.addCase('fetchData/fulfilled', (state, action) => {
state.data = action.payload;
state.loading = false;
})
.addCase('fetchData/rejected', (state, action) => {
state.error = action.error;
state.loading = false;
});
}
});
export const { /* ... your actions */ } = mySlice.actions;
export default mySlice.reducer;
builder: Provides methods likeaddCaseto define how to handle specific action types.addCase: Takes an action type string and a reducer function. The reducer function is called when that action is dispatched.
Benefits of createSlice
- Reduced Boilerplate: Significantly less code compared to manual Redux setup.
- Immutability: Immer handles immutable updates automatically.
- Type Safety: Works well with TypeScript.
- Readability: Clearer and more concise code.
- Maintainability: Easier to understand and modify.
- Automatic Action Generation: Action creators are generated for you.