Context 和Reducer实现跨组件状态管理

如果一个状态在多个组件中使用,并且一个组件改变状态后另一个组件会跟着更新,这种跨组件状态管理可以使用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>
  );
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容