Redux Toolkit is one of the libraries that can help us manage our application state which depends on Redux. It allows us to create reducers within our application and separate each reducer from one another based on any criteria that we desired in a specific kind of module. There are some approaches that we can utilize to build reducers in Redux Toolkit.
Basic Shape
const slice = createSlice({
// ...
reducers: {
increment: (state) => {
state += 1;
}
}
});
On the code above, it seems that we mutate the state, but actually, Redux Toolkit utilizes Immer in the backstage so it will be translated into immutable operation. Then, unlike legacy libraries that require us to manually define the action that will be dispatched to be handled by a specific reducer, Redux Toolkit can generate action creators for us.
const { increment } = slice.actions; // "increment" is the action creator
Then, we can dispatch the action inside our component following any activity we desired.
<button onClick={ () => dispatch(increment()) }>Add</button>
Basic Shape With Payload
Commonly we need to pass a payload to be processed by the reducer. It can be achieved by utilizing the second parameter in a reducer function.
const slice = createSlice({
// ...
reducers: {
incrementBy: (state, action: PayloadAction<number>) => {
state += action.payload;
}
}
});
Reducer With Pre-formatted Payload
Sometimes, we need to pre-format our payload before it is consumed by the reducer. We can declare our reducer as an object with specific properties rather than as a single function as the previous ones.
const slice = createSlice({
// ...
reducers: {
createArticle: {
reducer: (state, action: PayloadAction<any>) => {
// do something
},
prepare: (title, author) => ({
payload: {
id: uuid(),
title,
author,
createdOn: Date.now(),
}
}),
}
}
});
Then, we can pass the parameters which align with the configuration when we dispatch the action.
dispatch(createArticle(title, author));
Dispatching Action with Thunk Function
Originally, Redux Toolkit is integrated with thunk middleware that allows a function to be passed into a dispatch
call and to interact with the dispatch
and getState
methods inside it. It can be used to perform asynchronous logic before dispatching specific actions.
const sampleThunk = (dispatch, getState) => {
setTimeout(() => {
const currentState = getState();
dispatch(increment());
const nextState = getState();
}, 2000);
}
dispatch(sampleThunk)
If we want some data to be passed to the function, we can create a wrapper for that.
const sampleThunk = (amount) => (dispatch, getState) => {
setTimeout(() => {
const currentState = getState();
dispatch(incrementBy(amount));
const nextState = getState();
}, 2000);
}
dispatch(sampleThunk(10))
Asynchronous Thunk using createAsyncThunk
The createAsyncThunk
function will generate a thunk function that can provide a mechanism to interact with the store or auto-dispatch certain actions. It can be used when we want to monitor the states of asynchronous calls and then perform any action related to it. The function accepts two parameters, the first is the action name, and the second is the payload creator.
const getAllArticles = createAsyncThunk('article/getAllArticles', async (data?: any) => {
const response = await client.get('http://localhost:3000/article');
return response.data;
});
Now, the getAllArticles
function can be passed into a dispatch call.
dispatch(getAllArticles());
Besides that, the variable will be appended with additional action creator properties related to an asynchronous activity that can be read to initiate specific actions and handled by reducers. The properties are pending
, fullfiled
, and rejected
. Now, we can create extra reducers in our slice definition.
const slice = createSlice({
reducers: {
// ...
},
extraReducers: (builder) => {
builder
.addCase(getAllArticles.pending, (state) => {
// do something
})
.addCase(getAllArticles.fulfilled, (state, action: PayloadAction<Article[]>) => {
// do something
})
.addCase(getAllArticles.rejected, (state, action) => {
// do something
});
},
});
We can also perform additional actions in our component related to asynchronous results other than what is performed in the slice by using the unwrap
method which is available when the dispatch function is instantiated.
try {
await dispatch(getAllArticles()).unwrap()
// do something
} catch (err) {
console.error('Failed to get the articles: ', err)
} finally {
// do something
}
Comments
Post a Comment