useReducer
和useContext
前面有单独介绍过,上手不难,现在我们把这两个api结合起来使用,该怎么用?还是结合之前的简易增删改查的demo,熟悉vue
的应该可以看出,useReducer
类似于vuex
,useContext
类似于vue
中的inject
和provided
,来分析下思路。
实现效果
代码实现
- 文件拆解
- 组件入口文件 -> index.js
js
import { TasksContext, TasksDispatchContext } from "./context";
import { useReducer } from "react";
import { initialTasks } from "./taskLists";
import { taskReucers } from "./tasksReducer";
import AddTask from "./AddTask";
import TaskList from "./TaskList";
function State() {
const [tasks, dispatch] = useReducer(taskReucers, initialTasks);
return (
<TasksContext.Provider value={tasks}>
<TasksDispatchContext.Provider value={dispatch}>
<AddTask />
<TaskList />
</TasksDispatchContext.Provider>
</TasksContext.Provider>
);
}
export default State;
- AddTask.js
js
import { useState, useContext } from "react";
import { TasksDispatchContext } from "./context";
let nextId = 3;
function AddTask() {
let [msg, setMsg] = useState("");
const dispatch = useContext(TasksDispatchContext);
const handleSubmit = () => {
if (!msg) return;
setMsg("");
dispatch({
type: "added",
task: {
id: nextId++,
text: msg,
done: false,
},
});
};
const handleChange = (e) => {
setMsg(e.target.value);
};
return (
<>
<input type="text" value={msg} onChange={handleChange} />
<button onClick={handleSubmit}>添加</button>
</>
);
}
export default AddTask;
- context.js文件
js
import { createContext } from "react";
export const TasksContext = createContext(null);
export const TasksDispatchContext = createContext(null);
- taskReucers.js
js
export function taskReucers(state = [], action) {
switch (action.type) {
case "added":
return [...state, action.task];
case "changed":
return state.map((task) => {
if (task.id === action.task.id) {
return action.task;
} else return task;
});
case "deleted":
return state.filter((task) => task.id !== action.task.id);
default:
throw new Error("Action type not found");
}
}
- taskListsData.js
js
export const initialTasks = [
{ id: 0, text: "Philosopher's Path", done: true },
{ id: 1, text: "Visit the temple", done: false },
{ id: 2, text: "Drink matcha", done: false },
];
- TaskList.js
js
import { useState, useContext } from "react";
import Task from "./Task";
import { TasksContext } from "./context";
function TaskList() {
const [isEdit, setIsEdit] = useState(false);
const tasks = useContext(TasksContext);
const handleChangeValue = (value) => {};
return (
<ul>
{tasks.map((task) => {
return <Task key={task.id} task={task} />;
})}
</ul>
);
}
export default TaskList;
- Task.js
js
import { useState, useContext } from "react";
import { TasksDispatchContext } from "./context";
function Task({ task }) {
const [isEdit, setIsEdit] = useState(false);
const dispatch = useContext(TasksDispatchContext);
let todoContent = "";
function handleChangeText(e) {
dispatch({ type: "changed", task: { id: task.id, text: e.target.value } });
}
function handleDeleteTask(id) {
dispatch({ type: "deleted", task: { id } });
}
if (isEdit) {
todoContent = (
<span>
<input type="text" value={task.text} onChange={handleChangeText} />
<button onClick={() => setIsEdit(false)}>完成</button>
</span>
);
} else {
todoContent = (
<span>
<span>{task.text}</span>
<button onClick={() => setIsEdit(true)}>编辑</button>
</span>
);
}
return (
<li>
{todoContent}
<button onClick={() => handleDeleteTask(task.id)}>删除</button>
</li>
);
}
export default Task;
这样拆解后,明显的业务更加清晰,容易维护了,给后续接手的人一目了然的理解思路。
没有了组件层级之间的繁琐的层层传递数据和方法,代码结构也很清晰了。
进一步的优化业务代码
- context.js的封装
js
import { createContext, useReducer } from "react";
import { initialTasks } from "./taskListsData";
import { taskReucers } from "./tasksReducer";
export const TasksContext = createContext(null);
export const TasksDispatchContext = createContext(null);
export default function TasksProvider({ children }) {
const [tasks, dispatch] = useReducer(taskReucers, initialTasks);
return (
<TasksContext.Provider value={tasks}>
<TasksDispatchContext.Provider value={dispatch}>
{children}
</TasksDispatchContext.Provider>
</TasksContext.Provider>
);
}
- 入口文件index.js的优化
js
import AddTask from "./AddTask";
import TaskList from "./TaskList";
import TasksProvider from "./context";
function State() {
return (
<TasksProvider>
<AddTask />
<TaskList />
</TasksProvider>
);
}
export default State;
这样实现了同样的效果,代码更加精简。