本系列文以製作專案為主軸,紀錄小弟學習React以及GrahQL的過程。主要是記下重點步驟以及我覺得需要記憶的部分,有覺得不明確的地方還請留言多多指教。
簡介完Redux運作的概要後,進入實作,不過實作上跟上篇的流程有很大的差異,骨子裡相同但是會用 redux toolkit 的建立slice方法包裝起來用。
使用redux toolkit,裡面已經包含Redux:
npm install @reduxjs/toolkit
如果是新開的專案,可以在creat-react-app時選擇模板:
npx create-react-app my-app --template redux
在src底下開兩個資料夾,分別叫做container跟reducer。
reducer裡建立兩個檔案index.js跟todosSlice.js :
//index.js
import { combineReducers } from "redux";
import todosReducer from "./todosSlice";
export default combineReducers({
todos: todosReducer,
});
//todosSlice.js
import { createSlice } from "@reduxjs/toolkit";
const dummyData = [...]; //原本寫在KanBan.jsx裡的假資料
const todosSlice = createSlice({
name: "todos",
initialState: dummyData,
reducers: {},
});
export default todosSlice.reducer;
先從Slice開始介紹,createSlice是redux toolkit提供的方法,它可以在同一個方法中定義state、action、跟reducer,並根據定義產出reducer跟action creators。
用createSlice來生成slice:
Slice的用意在於把store切分成小塊管理,之後在把每個slice產生的reducer合併成一個root reducer後綁定給store。
而slice中定義的action 也會在前面帶上各自slice的名稱前綴,像是todos slice中的 addTodo 變成 "todos/addTodo",這樣一來如果不同slice中有同名的action,也會因為不同的前綴被store視為不同action,降低了action命名的複雜度。
雖然現在只有todos這個slice,不過為防以後新建更多的slice,要先用combineReducers包成一包。
export default combineReducers({
todos: todosReducer,
//未來的其他reducer加在這
});
接著我們到 src/index.js中,用剛剛包好的root reducer創建store並綁到App上。
import { configureStore } from "@reduxjs/toolkit";
import { Provider } from "react-redux";
// 從reducers/index取得combineReducers回傳的物件
import rootReducer from "./reducers";
const store = configureStore({ //初始化store並綁定reducer
reducer: rootReducer,
});
ReactDOM.render(
<React.StrictMode>
<Provider store={store}> //綁定store
<App />
</Provider>
</React.StrictMode>,
document.getElementById("root")
);
這樣在App底下就能存取到store了。
在containers資料夾底下建立 KanBanContainer.js
// KanBanContainer.js
import { connect } from "react-redux";
import KanBan from "../components/KanBan";
const mapStateToProps = (state) => ({
todos: state.todos, //將state 中的todos存為todos prop
});
//用connect將KanBan包上資料層,產生新的KanBanContainer部件
export default connect(mapStateToProps)(KanBan);
另外做一個container用意在於,區隔資料層(Data)與顯示層(View)。
KanBanContainer 負責處理資料相關的事情,並把state裡的todos傳給KanBan,KanBan則專心負責處理render需要的JSX部分。
接著在KanBan裡就能取得todos這個prop。
把lists 替換成 todos,清單就會改成用Redux store裡的資料顯示,不過別急著刪掉lists的useState,現在還有一大串葡萄依賴在上面,刪掉會大爆炸。
//KanBan.jsx
export default function KanBan({ todos }) {
//...
return (
<>
<KanBanNav />
<div className="board p-1">
{todos.map((list, index) => (...))} //替換掉lists
//...
</div>
</>
);
}
而在App.js裡,原本用的KanBan component要改為帶資料層的KanBanContainer。
//App.js
// 原本是 from "./components/KanBan";
import KanBan from "./containers/KanBanContainer";
function App() {
return (
<div className="App ">
<KanBan></KanBan>
</div>
);
}
這樣就成功讓部件從Redux的Store裡存取需要的資料,接著要一個個把原本更新lists的方法以todosSlice裡的reducer替代。
2020Ironman
React
tutorials