<LevelContext.Provider value={level + 1}>
可以讓包起來的value值遞增再複習一遍:Reducer是把「狀態更新的邏輯」集中起來管理,Context是讓組件更容易取值。
話不多說,開始實作Reducer + Context,先放添加鈕。確認連好「App.js」和「Add.js」後,我們在「Add.js」引入useState。
// Add.js
// 引入useState
const [text, setText] = useState("");
return (
<>
<input
placeholder="添加待辦"
value={text}
onChange={(e) => setText(e.target.value)}
/>
<button
onClick={() => {
setText("");
}}
>
添加
</button>
</>
);
現在的狀態很簡單,還只是個按下添加後會清空輸入欄的按鈕。
由於Reducer和Context最後會集中在一處,這時要到「Logic.js」引入createContext, useContext, useReducer。我們首先在「Logic.js」裡設置Reducer函數tasksReducer和初始資料initialTasks。這兩個接下來會被Context的Provider設為要傳遞的東西。
// Logic.js
// 設定Reducer函數
function tasksReducer(tasks, action) {
switch (action.type) {
case "added": {
return [
...tasks,
{
id: action.id,
text: action.text,
done: false,
},
];
}
case "changed": {
return tasks.map((t) => {
if (t.id === action.task.id) {
return action.task;
} else {
return t;
}
});
}
case "deleted": {
return tasks.filter((t) => t.id !== action.id);
}
default: {
throw Error("Unknown action: " + action.type);
}
}
}
// 設定初始狀態
const initialTasks = [
{ id: 0, text: "學習State", done: true },
{ id: 1, text: "學習Reducer", done: false },
{ id: 2, text: "學習Context", done: false },
];
設定Context的第一步是「創建」,亦即宣告Tasks和Dispatch是createContext(null)。這裡就像在宣告State和Setter是useState(0)一樣。
設定Context的第二步是「使用」。我們要用useContext把Tasks和Dispatch(這裡的Tasks和Dispatch是createContext時所創建的)包在組件裡傳出去。
設定Context的第三步是「提供」。所以我們加上TasksProvider,把Reducer函數和初始資料用task和dispatch宣告起來,然後在return時賦予給{ children }。
// Logic.js
// 「創建」Context
const Tasks = createContext(null);
const Dispatch = createContext(null);
// 「提供」Context
export function TasksProvider({ children }) {
const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);
return (
<Tasks.Provider value={tasks}>
<Dispatch.Provider value={dispatch}>{children}</Dispatch.Provider>
</Tasks.Provider>
);
}
// 「使用」Context
export function useTasks() {
return useContext(Tasks);
}
export function useDispatch() {
return useContext(Dispatch);
}
於是可以回到「Add.js」。當然要記得import { useDispatch } from "./Logic.js"。然後在組件裡const dispatch = useDispatch(),並派遣東西給按鈕。
這裡有個隱密的坑,那就是「App.js」也要記得改啊!從return <Add />
改成
return (
<TasksProvider>
<h1>React待辦事項</h1>
<Add />
</TasksProvider>
);
不過現在還看不到加了什麼。所以最後來完成「List.js」吧。這邊就是用到各種State觀念了。
然後可以對照一下「List.js」和Reducer函數的code,可以更注意到Reducer是收到action之後,在不同type之間switch的。
最後附上初始資料傳遞路徑圖: