Lala Code

Lala 的前端大補帖,歡迎一起鑽研前端技術😊

0%

【Redux Toolkit 教學】管理組件的共用資料

Redux Toolkit

Redux 是可以讓不同的組件,使用到共用的資料和方法,統一集中管理共用的狀態
Redux 不是 React 的 library,但很多 React 專案都會使用 Redux 來管理狀態,這也是學 React 比較複雜的地方🤪
好在官方之後出了 Redux Toolkit,也推薦大家使用 Redux Toolkit 來撰寫 Redux,它是以 Redux 為核心,也比較容易上手!



安裝環境

可依自己的需求安裝環境

創建模板 React + Redux Toolkit

1
npx create-react-app my-app --template redux

安裝 react-redux、@reduxjs/toolkit

1
npm i react-redux @reduxjs/toolkit



認識變數

使用 Redux,你會聽到幾個關鍵字:

  1. state:用來存放資料狀態
  2. reducer:用來放改變 state 狀態的方法
  3. action:reducer 要修改 state 的話,需要傳入 action ,去判斷要啟動哪個 reducer
  4. Provider:在所有組件的最外面(通常是 Index.js)包一層 Provider,傳入 store,所有被包覆的組件都可以使用到 store 的狀態。
  5. store:存放 state、reducer、action 的檔案


Redux 流程

  1. 創建 Store
  2. 在最外層加入 Provider,並傳入 store,使整個組件都能使用 store 資料
  3. 創建 Slice,設定 state、reducer、action
  4. 畫面渲染,取得 state、dispatch 方法改變狀態


範例情境:

todoList 列表,點擊按鈕後新增下一條


創建 store

store/index.js

1
2
3
4
5
6
7
8
import { configureStore } from "@reduxjs/toolkit";
import todoReducer from "./slice/todo";

export default configureStore({
reducer: {
todo: todoReducer,
},
});

configureStore:創建 store 的參數

原生為 creactStore,使用 creactStore 時會有被棄用的警告

如有多個檔案,一樣 import 進來

1
2
3
4
5
6
7
8
9
10
import { configureStore } from "@reduxjs/toolkit";
import todoReducer from "./slice/todo";
import productReducer from "./slice/product";

export default configureStore({
reducer: {
todo: todoReducer,
product: productReducer
},
});



在最外層加入 Provider

src/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
import { Provider } from "react-redux";
import store from "./store/index";

const container = document.getElementById('root');
const root = createRoot(container);

root.render(
<Provider store={store}>
<App />
</Provider>
);

也可以加在 App.js 的最外層,只要是外層都可以


創建 Slice

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { createSlice } from "@reduxjs/toolkit";

export const todoSlice = createSlice({
name: "todo",
initialState: {
todolist: [
{ id: 1, name: "早上帶波比散步" },
{ id: 2, name: "中午帶波比散步" },
{ id: 3, name: "晚上帶波比散步" },
{ id: 4, name: "睡前帶波比散步" },
],
},
reducers: {
addTodo: (state, action) => {
state.todolist.push(action.payload);
},
},
});

export const { addTodo } = todoSlice.actions; // 取用方法
export const selectTodo = (state) => state.todo; // 取用資料
export default todoSlice.reducer;

createSlice:創建slice,把 Redux 原生的 state、reducer、action 都合在一包,稱之為 slice

name:取一個相關的名稱

initialState:所有狀態的初始值

reducers:存放函式,傳入兩個參數,第一個為需要修改的 state,第二個為 action 讓你傳入的參數


畫面渲染

component/TodoList.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React from "react";
import { useSelector } from "react-redux";
import { selectTodo } from "../store/slice/todo";
const TodoList = () => {
const states = useSelector(selectTodo);
return (
<ul>
{states.todolist.map((i) => (
<li key={i.id}>{i.name}</li>
))}
</ul>
);
};

export default TodoList;

useSelector:要拿取 state,就要使用 Redux 的 useSelector api,傳入你在 slice 建立的 Selector


引用組件到 App.js,dispatch 取得 store 方法

App.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { useDispatch, useSelector } from "react-redux";
import TodoList from "./components/TodoList";
import { addTodo } from "./store/slice/todo";

function App() {
const dispatch = useDispatch();

const handleAddTodo = () => {
dispatch(
addTodo({ id: new Date().getTime(), name: "帶波比去公園"})
)
};

return (
<div>
<TodoList />
<button onClick={handleAddTodo}>add todo</button>
</div>
);
}

export default App;

非同步操作

做前端一定會碰到 API 的串接,我們需要 createAsyncThunk 方法

我在 public 新增一個要被呼叫的 json

public/todolist.json

1
2
3
4
5
6
7
8
{
"data": [
{ "id": 1, "text": "早上帶波比散步" },
{ "id": 2, "text": "中午帶波比散步" },
{ "id": 3, "text": "晚上帶波比散步" },
{ "id": 4, "text": "睡前帶波比散步" }
]
}

並使用 axios 串接 API

api/index.js

1
2
3
4
import axios from "axios";
export const getData = () => {
return axios.get('/index.json');
}

在 slice 的地方引用,並使用 createAsyncThunk 操作非同步

store/slice/todo.js

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
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { getData } from "../../api";

export const fetchData = createAsyncThunk('todo/fetchTodo', async () => {
const response = await getData();
return response.data.data
})

export const todoSlice = createSlice({
name: "todo",

initialState: {
loading: false,
entities: []
},

reducers: { },

extraReducers: {
[fetchData.pending]: (state) => {
state.loading = true
},

[fetchData.fulfilled]: (state, { payload }) => {
state.loading = false
state.entities = payload
},

[fetchData.rejected]: (state) => {
state.loading = false
},
},
});

export const selectTodo = (state) => state.todo;
export default todoSlice.reducer;

createAsyncThunk:在 Redux Toolkit 要呼叫非同步,需要使用 createAsyncThunk 方法

createAsyncThunk 接受兩個參數,第一個為 action type 字串,第二個為返回的 Promise,並生成一個pendingfulfilledrejected ,分派 action type 的 thunk

extraReducers:因為非同步的函式不在 createSlice 裡面,如果要在 createSlice 中監聽這些 action type,需要在 extraReducers 使用


dispatch 呼叫方法,並取得 api 回傳的資料

App.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { fetchData, selectTodo } from "./store/slice/todo";

function App() {
const dispatch = useDispatch();
const { entities, loading } = useSelector(selectTodo);

useEffect(() => {
dispatch(fetchData());
}, [])

if (loading) return <p>Loading...</p>

return (
<div>
{entities.map(item => (<div key={item.id}>{item.text}</div>))}
</div>
);
}

export default App;



⚠️eslint 出現警告

使用 useEffect 時遇到警告錯誤

1
2
React Hook useEffect has a missing dependency: 'dispatch'.
Either include it or remove the dependency array

如果想除掉警告可以把 dispatch 放到第二個參數的陣列裡
1
2
3
useEffect(() => {
dispatch(fetchData());
}, [dispatch])

參考資料 React + React-Redux + Redux-Toolkit 新手教學Using Redux Toolkit’s createAsyncThunk



Hey!想學習更多前端知識嗎?

最近 Lala 開了前端課程 👉【實地掌握RWD - 12小時新手實戰班】👈
無論您是 0 基礎新手,又或是想學 RWD 的初學者,
我們將帶你從零開始,深入了解並掌握 RWD 響應式網頁設計的核心技術,快來一起看看吧 😊



🚀線上課程分享

線上課程可以加速學習的時間,省去了不少看文件的時間XD,以下是我推薦的一些課程
想學習更多關於前後端的線上課程,可以參考看看。

Hahow

Hahow 有各式各樣類型的課程,而且是無限次數觀看,對學生或上班族而言,不用擔心被時間綁住



六角學院

如果你是初學者,非常推薦六角學院哦!
剛開始轉職也是上了六角的課,非常的淺顯易懂,最重要的是,隨時還有線上的助教幫你解決問題!


Udemy

Udemy 裡的課程非常的多,品質普遍不錯,且價格都滿實惠的,CP值很高!
也是很多工程師推薦的線上課程網站。
❤️