Redux Toolkit & Redux Saga

Getting Start Over the years the javascript world has evolved greatly, new libraries and frameworks pop up and existing ones have improve a lot and that is especially true to React as well. Among the vast majority of library, redux is a popular one that developer usually used along side when they develop a React

Getting Start

Over the years the javascript world has evolved greatly, new libraries and frameworks pop up and existing ones have improve a lot and that is especially true to React as well. Among the vast majority of library, redux is a popular one that developer usually used along side when they develop a React application. Many others utility and devtool libraries also being used, which makes setting up a React project a very daunting task. As the pattern grow very common, a community has bundle all those together and thus redux-toolkit was borned.

Redux Toolkit

Install Package

To get start with redux toolkit, first lets install it

$ npx create-react-app app-name --template redux # javascript
$ npx create-react-app app-name --template redux-typescript # typescript
$ # or using yarn
$ yarn create react-app --template redux
$ yarn create react-app --template redux-typescript

Or add it into existing project

$ npm install @reduxjs/toolkit # or using yarn
$ yarn add @reduxjs/toolkit

The Basic

Lets start from our redux store in src/app/store.js. It will looks like this

import{ configureStore }from'@reduxjs/toolkit';exportconst store =configureStore({
  reducer:{},});

Lets add a todo feature into our app, start from todoReducer

// src/features/todo/todoSlice.jsimport{ createSlice }from'@reduxjs/toolkit';const initialState ={
  todos:[],
  status:'idle'};exportconst todoSlice =createSlice({
  name:'todo',
  initialState,
  reducers:{addTodo:(state, action)=>{
      state.todos.push(action.payload);},removeTodo:(state, action)=>{const index = state.todos.findIndex((todo)=> todo.id === action.payload.id);if(index !==-1){
        state.todos.splice(index,1);}}}});exportconst{ addTodo, removeTodo }= todoSlice.actions;exportconstselectTodo=(state)=> state.todo;exportdefault todoSlice.reducer;

The slice is a place where your reducer and action that will cause redux’s state to change live. createSlice takes the name of the slice of the state tree, initial state value and reducer function to define how the state will update. These reducer function will become action creator function to be used inside of component.

Also notice that redux require that we write all updates immutably, but our reducer function mutate the state directly that’s because createSlice API used Immer object internally.

Then in src/app/store.js add the following lines

import{ configureStore }from'@reduxjs/toolkit';import counterReducer from'../features/todo/todoSlice'// new lineexportdefaultconfigureStore({
  reducer:{
    todo: todoSlice // new line},});

Using in Component

Let’s take a look at how to use it in component. The code is simple first we use useSelector to select the todo from a slice of our state tree by using selector function import from our previous file. Then in the component we dispatch addTodo and removeTodo in respond to Add todo and Delete button click respectively. When addTodo and removeTodo are dispatch our reducer function define in todoSlice with the same name will be trigger and update our state tree accordingly.

// src/features/TodoList.jsximport React,{ useState }from'react';import{ useDispatch, useSelector }from'react-redux';import{ selectTodo, addTodo, removeTodo }from'./todoSlice';functionTodo({ todo, onDelete }){constonClick=(event)=>{
    event.preventDefault();onDelete(todo.id);};return(<li><span>{todo.title}</span><a href="#" onClick={onClick}>Delete</a></li>);}exportdefaultfunctionTodoList(){const dispatch =useDispatch();const{ todos }=useSelector(selectTodo);const[todo, setTodo]=useState({ id:1, title:''});constonChange=(event)=>{const{ value }= event.target;setTodo({...todo, title: value });};constonClick=()=>{dispatch(addTodo(todo));setTodo({ id: todo.id +1, title:''});};constonDelete=(id)=>{dispatch(removeTodo({ id }));};return(<div><input type="text" onChange={onChange} value={todo.title}/><button onClick={onClick}>Add todo</button><ul>{todos.map((todo)=>(<Todo key={todo.id} todo={todo} onDelete={onDelete}/>))}</ul></div>);}

Async Action

Redux Toolkit come with async action handling by default using redux-thunk. To create an async action creator we use createAsyncThunk like follow.

// src/features/todo/todoSlice.jsimport{ createSlice, createAsyncThunk }from'@reduxjs/toolkit';functionaddTodoAPI(payload){returnnewPromise((resolve, reject)=>{setTimeout(()=>resolve(payload),1000);});}exportconst addTodoAsync =createAsyncThunk('todo/addTodoAsync',async(payload)=>{const data =awaitaddTodoAPI(payload);return data;});exportconst todoSlice =createSlice({...,extraReducers:(builder)=>{
    builder
      .addCase(addTodoAsync.pending,(state)=>{
        state.status ='pending';}).addCase(addTodoAsync.fulfilled,(state, action)=>{
        state.status ='idle';
        state.todos.push(action.payload);});}});

createAsyncThunk will return a function with three actions: pending, fulfilled and rejected. These actions are correspond to each state of the API. In our example above we mimic the delay of response from server by using setTimeout.

Using Redux Saga

If you prefer handling async action using redux-saga then we need to modify and create a custom middleware when create our store like follow

// src/app/store.jsimport createSagaMiddleware from'redux-saga';import{ configureStore, getDefaultMiddleware }from'@reduxjs/toolkit';import counterReducer from'../features/todo/todoSlice';import rootSaga from'./saga';// disalbe thunk and add redux-saga middlewareconst sagaMiddleware =createSagaMiddleware();const middleware =[...getDefaultMiddleware({ thunk:false}), sagaMiddleware];exportdefaultconfigureStore({
  reducer:{
    todo: todoSlice
  },
  middleware
});

sagaMiddleware.run(rootSaga);// src/app/saga.jsimport{ all }from'redux-saga/effects';import{ todoSaga }from'../features/todo/todoSlice';exportdefaultfunction*rootSaga(){yieldall([todoSaga()]);}

Then in todoSlice.js

import{ call, put, takeLatest }from'redux-saga/effects';import{ createAction }from'@reduxjs/toolkit';exportconst addTodoAsync =createAction('todo/addTodoAsync');function*addTodoSaga(action){const data =yieldcall(addTodoAPI, action.payload);yieldput(addTodo(data));}exportfunction*todoSaga(){yieldtakeLatest(addTodoAsync, addTodoSaga);}// rest of the code

Conclusion

In this article we’ve learned the basic of how to use redux toolkit as well as configuring it to our need by switch some of the core library, redux-thunk with redux-saga. I hope you find this is helpful.

Nguồn: viblo.asia

Bài viết liên quan

9 Mẹo lập trình Web “ẩn mình” giúp tiết kiệm hàng giờ đồng hồ

Hầu hết các lập trình viên (kể cả những người giỏi) đều tốn thời gian x

Can GPT-4o Generate Images? All You Need to Know about GPT-4o-image

OpenAI‘s GPT-4o, introduced on March 25, 2025, has revolutionized the way we create visual con

Khi nào nên dùng main, section, article, header, footer, và aside trong HTML5

HTML5 đã giới thiệu các thẻ ngữ nghĩa giúp cấu trúc nội dung web một cách có

So sánh Webhook và API: Khi nào nên sử dụng?

Trong lĩnh vực công nghệ thông tin và phát triển phần mềm, Webhook và API là hai th