useReducer 实际上是以数组上的 reduce() 方法命名的。
传递给 reduce 的函数被称为 “reducer”。它接受 目前的结果 和 当前的值,然后返回 下一个结果。
React 中的 reducer 和这个是一样的:它们都接受 目前的状态 和 action ,然后返回 下一个状态。
reduce
const arr = [1, 2, 3, 4, 5];
const sum = arr.reduce(
(result, number) => result + number
); // 1 + 2 + 3 + 4 + 5
useReducer
// 定义 reducer
const reducer = (state, action){
const { type, payload } = action
// case 块包装到 { } 花括号中,这样在不同 case 中声明的变量就不会互相冲突。
// case 通常应该以 return 或 break 结尾,避免代码进入 到下一个 case!
switch(type){
case 'add': {
return state + payload.num
}
case 'substract': {
return state - payload.num
}
default: {
throw Error('未知 action: ' + action.type);
}
}
}
// 声明 useReducer
const [tasks, dispatch] = useReducer(reducer, 0);
// 使用 useReducer
function handleAddTask(text) {
dispatch({
type: 'add',
payload: {
num: 100
}
});
}
useReduce 同 useState一样, 返回的state是不能直接修改的, 而 Immer 提供了一种特殊的 draft 对象,你可以通过它安全的修改 state。在底层,Immer 会基于当前 state 创建一个副本。
useImmerReducer
// 引入
import { useImmerReducer } from 'use-immer';
// 声明
const initialTasks = [
{id: 0, text: '参观卡夫卡博物馆', done: true},
{id: 1, text: '看木偶戏', done: false},
{id: 2, text: '打卡列侬墙', done: false},
];
const [tasks, dispatch] = useImmerReducer(tasksReducer, initialTasks);
// 定义 reducer
function tasksReducer(draft, action) {
switch (action.type) {
case 'added': {
draft.push({
id: action.id,
text: action.text,
done: false,
});
break;
}
case 'changed': {
const index = draft.findIndex((t) => t.id === action.task.id);
draft[index] = action.task;
break;
}
case 'deleted': {
return draft.filter((t) => t.id !== action.id);
}
default: {
throw Error('未知 action:' + action.type);
}
}
}
// 使用 useReducer
function handleAddTask(text) {
dispatch({
type: 'added',
id: nextId++,
text: text,
});
}