使用 Redux Toolkit (RTK) 可以簡化傳統 Redux 的重複性操作。目前 Redux 也推薦使用 RTK。
RTK 創建 reducer 及生成對應 action
安裝 RTK 包 npm install @reduxjs/toolkit
。
在 reducer 下創建 slice 檔案,使用 createSlice 創建 reducer 及 action。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| import { createSlice } from '@reduxjs/toolkit'; export const todoSlice = createSlice({ name: 'todos', initialState: { todoArr: [ { userId: 1, id: 1, title: 'delectus aut autem', completed: false, } ] }, reducers: { deleteTodo(state, action) { return state.todoArr.filter((item) => item.id !== action.payload); }, }, });
console.log(todoSlice.actions);
export const { deleteTodo } = todoSlice.actions;
|
導入並透過 RTK 的 configureStore 來構建資料庫。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import { configureStore } from '@reduxjs/toolkit';
import { todoSlice } from './reducers/todoSlice';
export default configureStore({ reducer: { todos: todoSlice.reducer, }, });
|
- 在組件中使用 react-redux Hooks 取得或變更數據。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| import { useSelector, useDispatch } from 'react-redux';
import { deleteTodo } from './01.redux/reducers/todoSlice';
export default function Home() { const todoArr = useSelector((state) => { console.log(state); return state.todos.todoArr; }); const dispatch = useDispatch(); const deleteItem = (id) => { dispatch(deleteTodo(id)); }; return ( <div> 我是Home,放置所有 todo 資料 <hr /> <ul> {todoArr.map((item) => ( <li key={item.id}> {item.title} <button onClick={(e) => { deleteItem(item.id); }}> 刪除 </button> </li> ))} </ul> </div> ); }
|
RTK 異步數據請求
- 在 slice.js 中透過 createAsyncThunk 來處理異步數據。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import {createAsyncThunk, createSlice} from '@reduxjs/toolkit';
export const fetchTodoArr = createAsyncThunk({ "todos/fetchTodoArr", async(args, thunkAPI) => { const res = await fetch(`https://jsonplaceholder.typicode.com/todo`); if (res.status === 404) { return thunkAPI.rejectWithValue("獲取數據失敗"); } const json = await res.json(); return json; } })
|
- 在 createSlice 中會有一個屬性 extraReducers 用來處理異步 action 傳回的資料。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| export todoSlice = createSlice({ reducers: { }, extraReducers: { [fetchTodoArr.fulfilled]: (state, action) => { state.todoArr = action.payload; }, [fetchTodoArr.rejected]: (state, action) => { state.status = 404; state.errorText = action.payload; } } })
|
- 在組件中依然是透過 react-redux Hooks (useSelector、useDispatch)來獲取資料。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| import { useEffect } from 'react'; import { useSelector, useDispatch } from 'react-redux';
import { deleteTodo, fetchTodoArr } from './01.redux/reducers/todoSlice';
export default function Home() { const todoArr = useSelector((state) => state.todos.todoArr); const todoStatus = useSelector((state) => state.todos.status); const todoErrText = useSelector((state) => state.todos.errorText);
const dispatch = useDispatch();
useEffect(() => { if (todoArr.length === 0) { dispatch(fetchTodoArr()); } }, []); return ( <div> 我是Home,放置所有 todo 資料 <hr /> <ul> {todoStatus !== 200 && todoArr.length === 0 ? ( <p>{todoErrText}</p> ) : ( todoArr.map((item) => ( <li key={item.id}> {item.title} <button onClick={(e) => { deleteItem(item.id); }}> 刪除 </button> </li> )) )} </ul> </div> ); }
|