react18中实现简易增删改查useReducer搭配useContext的高级用法

useReduceruseContext前面有单独介绍过,上手不难,现在我们把这两个api结合起来使用,该怎么用?还是结合之前的简易增删改查的demo,熟悉vue的应该可以看出,useReducer类似于vuexuseContext类似于vue中的injectprovided,来分析下思路。

实现效果

代码实现

  • 文件拆解
  • 组件入口文件 -> 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;

这样实现了同样的效果,代码更加精简。

相关推荐
子兮曰2 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰2 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万2 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝2 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋2 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁2 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
汉堡大王95272 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大2 天前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端
计算机魔术师2 天前
Meta Muse agent 接入 Shopify 的 Shop Pay 实现代理式购物
前端
沙蒿同学2 天前
我用 Go 搭了一条 AI Agent 流水线:从 1 张商品图到一整套淘宝详情页
前端·javascript·后端