I have learned about redux toolkit for 2 months, and in createSlice have reducers and extrareducers, I know they use to change state from dispatch but I don't know the difference, Where should we use them?
I have learned about redux toolkit for 2 months, and in createSlice have reducers and extrareducers, I know they use to change state from dispatch but I don't know the difference, Where should we use them?
The reducers property both creates an action creator function and responds to that action in the slice reducer. The extraReducers allows you to respond to an action in your slice reducer but does not create an action creator function.
You will use reducers most of the time.
You would use extraReducers when you are dealing with an action that you have already defined somewhere else. The most common examples are responding to a createAsyncThunk action and responding to an action from another slice.
extrareducers is actually like reducers with enhancements, but it's been built to handle more options, esecially other actions (like the ones generated in other slices or actions made by createAction or createAsyncThunk). In one word
Everything you can dispatch in redux can be added to it.
The extrareducers property in createSlice can be used as a function or as an object.
The function form has an input argument named builder.
Example 1: The builder.addCase function adds an action from another slice (with Typescript type safety).
const {actions,reducer} = createSlice({
name:'users',
reducers:{
......
},
initialState:.... ,
extraReducers:(builder) => {
builder.addCase(authActions.logout, state => {
state.isLoggedIn = false;
});
}
});
The authActions.logout here is another action from another slice.
If you create an action using createAsyncThunk function (which is imported from "@reduxjs/toolkit") You can handle loading, success & failure states.
Example 2: Handling loading state from an async Thunk:
const fetchUserById = createAsyncThunk(
'users/fetchByIdStatus',
async (userId, thunkAPI) => {
const response = await userAPI.fetchById(userId)
return response.data
}
)
const {actions,reducer} = createSlice({
name:'users',
reducers:{
......
},
initialState:.... ,
extraReducers: (builder) => {
......
......
builder.addCase(fetchUserById.pending, (state, action) => {
state.loading=true;
state.whoseDataIsLoading = action.payload;
})
}
});
The builder also accepts addDefaultCase and addMatcher in which the addDefaultCase acts as the default case in switch statement used by conventional reducers (reducers in the redux without toolkit) and,
addMatcher is used with a type predicate function a function used to infer that some input has some particular type (type of action in here) to ensure the action has a particular type, then the state changes.
Example 3: The extrareducers as an object:
const {actions,reducer} = createSlice({
name:'users',
reducers:{
......
},
initialState:.... ,
extraReducers: {
['incrementBy']: (state, action) => {
state.someValue += action.payload
}
}
});
References:
If action is supposed to be handled by one reducer, use reducers.
If action is supposed to be handled by multiple reducers, use extraReducers.
From the docs:
As case reducers specified with extraReducers are meant to reference "external" actions, they will not have actions generated in slice.actions.
Let's say I defined a reducer inside reducers for "postsSlice"
reducers: {
setEdit: (state, action) => {
state.edit = action.payload.edit;
},
},
I can destructure this. Because this belongs to current slice.actions
export const { setEdit } = postsSlice.actions;
but if you create an action
export const getPost = createAsyncThunk("post/getPost", async ({ id }) => {
return fetch(`https://jsonplaceholder.typicode.com/posts/${id}`).then((res) =>
res.json()
);
});
this will be inside extraReducers.
If you console.log("postSlcie.actions", postsSlice.actions) you wont see that action in postsSlice.actions