1,useReducter
作用:useReducer是state的另一种用法,用来管理状态。他避免了大量state的繁杂逻辑,更容易管理。例如以下情况:
import { useState } from 'react';
export default function TaskApp() {
const [tasks, setTasks] = useState(initialTasks);
// 新增操作
function handleAddTask(text) {
setTasks([
...tasks,
{ id: nextId++, text: text },
]);
}
// 修改操作
function handleEditTask(task) {
setTasks(
tasks.map((t) => {
if (t.id === task.id) {
return task;
} else {
return t;
}
})
);
}
// 删除操作
function handleDeleteTask(taskId) {
setTasks(tasks.filter((t) => t.id !== taskId));
}
return (
<>
<h1>布拉格的行程安排</h1>
<AddTask onAddTask={handleAddTask} />
<Task tasks={tasks}
onChangeTask={handleEditTask}
onDeleteTask={handleDeleteTask}
/>
</>
);
}
一个行数据既可以编辑、删除,又可以新增等多种逻辑操作,useState代码逻辑就会显得臃肿。
使用方法:
(1)将设置状态的逻辑(setXXX)修改成dispatch的一个action
主要语法是dicpatch({ type: '具体事件类型', 自定义的其他字段 });
function handleAddTask(text) {
dispatch({
type: 'add',
id: nextId++,
text: text,
});
}
function handleEditTask(task) {
dispatch({
type: 'edit',
task: task,
});
}
function handleDeleteTask(taskId) {
dispatch({
type: 'delete',
id: taskId,
});
}
(2)编写一个reducer函数,类似于js中封装逻辑的函数。他接受两个参数:state和action。
function tasksReducer(tasks, action) {
switch (action.type) {
case 'add': {
return [
...tasks,
{
id: action.id,
text: action.text
},
];
}
case 'edit': {
return tasks.map((t) => {
if (t.id === action.task.id) {
return action.task;
} else {
return t;
}
});
}
case 'delete': {
return tasks.filter((t) => t.id !== action.id);
}
default: {
throw Error('未知 action: ' + action.type);
}
}
}
(3)在组件中使用reducer
引入useReducer替换useState:
import { useReducer } from 'react';
初始化reducer替换state:
const [tasks, dispatch] = useReducer(tasksReducer, [
{id: 0, text: '参观卡夫卡博物馆'},
// ...
]);
useReducer()的第一个参数tasksReducer就是自定义的Reducer函数
扩展:
reducer同样可以用Immer来简化,首先引入import { useImmerReducer } from 'use-immer';
然后也通过draft参数来修改数据:
function tasksReducer(draft, action) {
switch (action.type) {
case 'add': {
draft.push({
id: action.id,
text: action.text
});
break;
}
case 'edit': {
const index = draft.findIndex((t) => t.id === action.task.id);
draft[index] = action.task;
break;
}
case 'delete': {
return draft.filter((t) => t.id !== action.id);
}
default: {
throw Error('未知 action:' + action.type);
}
}
}
2,useContext
作用:
useContext是props传参的另一种用法,他避免了深层嵌套的props和多个相同组件使用的冗余props。例如以下情况:
return (
<Section level={1}>
<Heading level={1}>主标题</Heading>
<Heading level={1}>主标题</Heading>
<Heading level={1}>主标题</Heading>
</Section>
)
父组件和多个子组件都要分别传入相同的props“level”,而使用useContext可以解决这种代码冗余
使用方法:
(1)创建Context
import { createContext } from 'react';
const LevelContext = createContext(initial);
(2)使用Context
import { useContext } from 'react';
const level = useContext(LevelContext);
(3)使用Context Provider包裹他所有的子组件,并提供Context
// 目标外层组件Section,使用创建的Context值LevelContext.Provider包裹
export default function Section({ level, children }) {
return (
<section className="section">
<LevelContext.Provider value={level}>
{children}
</LevelContext.Provider>
</section>
);
}
解释:使用第1步创建的Context值LevelContext.Provider来包裹所有的子组件,这样<Section>组件中的任何子组件请求LevelContext时,都从最近的父组件LevelContext.Provider获取传递的value值level。因此useContext同样可以解决深层嵌套,祖孙组件的传值。
适用的场景:
- 更改主题(每个子组件样式都要改变)
- 路由
- 状态管理(深层的state,且可能需要修改state,通常会与useReducer一起搭配使用)
3,useReducer和useContext结合使用
二者可以配合使用:
(1)初始化useReducer,创建两个Context分别对应state和dispatch
// 初始化reducer
import { useReducer } from 'react';
const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);
// 创建两个context分别对应state和dispatch
import { createContext } from 'react';
export const TasksContext = createContext(null);
export const TasksDispatchContext = createContext(null);
(2)将state和dispatch放入Context(两个context顺序包裹,为整个组件树提供state和dispatch)
import { TasksContext, TasksDispatchContext } from './TasksContext.js';
// ...
// 包含第一步的初始化reducer逻辑:const [tasks, dispatch] = ...
return (
<TasksContext.Provider value={tasks}>
<TasksDispatchContext.Provider value={dispatch}>
...
</TasksDispatchContext.Provider>
</TasksContext.Provider>
);
// ...
(3)在组件树的任何地方使用context
// 以包裹一个AddTask组件为例
import { TasksContext, TasksDispatchContext } from './TasksContext.js';
export default function TaskApp() {
const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);
// ...
return (
<TasksContext.Provider value={tasks}>
<TasksDispatchContext.Provider value={dispatch}>
<AddTask />
</TasksDispatchContext.Provider>
</TasksContext.Provider>
);
}
// 在AddTask组件中应用dispatch
import { useContext } from 'react';
import { TasksDispatchContext } from './TasksContext.js';
export default function AddTask() {
const dispatch = useContext(TasksDispatchContext);
// ...
return (
// ...
<button onClick={() => {
dispatch({
type: 'add',
id: nextId++,
text: text,
});
}}>Add</button>
}
说明:二者的结合可以在大型应用中大量应用,同时还可以配合部分useState灵活应用