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

WebP là gì? Hướng dẫn cách để chuyển hình ảnh jpg, png qua webp

WebP là gì? WebP là một định dạng ảnh hiện đại, được phát triển bởi Google

Điểm khác biệt giữa IPv4 và IPv6 là gì?

IPv4 và IPv6 là hai phiên bản của hệ thống địa chỉ Giao thức Internet (IP). IP l

Check nameservers của tên miền xem website trỏ đúng chưa

Tìm hiểu cách check nameservers của tên miền để xác định tên miền đó đang dùn

Mình đang dùng Google Domains để check tên miền hàng ngày

Từ khi thông báo dịch vụ Google Domains bỏ mác Beta, mình mới để ý và bắt đầ