如果一个状态在多个组件中使用,并且一个组件改变状态后另一个组件会跟着更新,这种跨组件状态管理可以使用Context 结合Reducer实现。
在最外面的父组件中使用Context向下传递一个列表值tasks和修改值的方法dispatch:
useReducer 传入默认值和修改数据的方法,会返回方法中返回的最新值,还有dispath方法。
import React, { useReducer, useMemo } from 'react';
import AddTask from './components/AddTask';
import TaskList from './components/TaskList';
import { TaskContext } from './components/TaskContext.js';
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;
}
return t;
});
}
case 'deleted': {
return tasks.filter((t) => t.id !== action.id);
}
default: {
throw Error(`Unknown action: ${action.type}`);
}
}
}
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 }
];
export default function ChildIndex() {
const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);
const contextValue = useMemo(() => ({ tasks, dispatch }), [tasks, dispatch]);
return (
<TaskContext.Provider value={contextValue}>
<h1>Day off in kyoto</h1>
<AddTask />
<TaskList />
</TaskContext.Provider>
);
}
其中一个子组件会修改列表,使用context传递的dispath方法修改数据:
import React, { useState, useContext } from 'react';
import { message } from 'antd';
import { TaskContext } from './TaskContext';
let nextId = 3;
export default function AddTask() {
const [text, setText] = useState('');
const { dispatch } = useContext(TaskContext);
return (
<div>
<input placeholder="Add task" value={text} onChange={(e) => setText(e.target.value)} />
<button
type="button"
onClick={() => {
if (text) {
setText('');
dispatch({
type: 'added',
id: nextId++,
text
});
} else {
message.success('不能添加空值');
}
}}
>
Add
</button>
</div>
);
}
另一个组件会展示最新的列表数据:
import React, { useState, useContext } from 'react';
import { TaskContext } from './TaskContext';
export default function TaskList() {
const { tasks } = useContext(TaskContext);
return (
<ul>
{tasks.map((task) => (
<li key={task.id} style={{ margin: '10px 0' }}>
<Task task={task} />
</li>
))}
</ul>
);
}
TaskList中的子组件Task也会修改数据:
interface TaskProps {
task: { id: number; text: string; done: boolean };
}
function Task({ task }: TaskProps) {
const { dispatch } = useContext(TaskContext);
const [isEditing, setIsEditing] = useState(false);
let taskContent;
if (isEditing) {
taskContent = (
<>
<input
value={task.text}
onChange={(e) => {
dispatch({
type: 'changed',
task: {
...task,
text: e.target.value
}
});
}}
/>
<button style={{ margin: '0 10px' }} type="button" onClick={() => setIsEditing(false)}>
Save
</button>
</>
);
} else {
taskContent = (
<>
{task?.text}
<button type="button" style={{ margin: '0 10px' }} onClick={() => setIsEditing(true)}>
Edit
</button>
</>
);
}
return (
<label>
<input
type="checkbox"
checked={task.done}
onChange={(e) => {
dispatch({
type: 'changed',
task: {
...task,
done: e.target.checked
}
});
}}
/>
{taskContent}
<button
onClick={() => {
dispatch({
type: 'deleted',
id: task.id
});
}}
>
Delete
</button>
</label>
);
}