当前位置:首页 > React

react实现撤销恢复

2026-01-26 18:42:06React

实现撤销/恢复功能的核心思路

在React中实现撤销和恢复功能通常需要维护一个状态历史记录,并跟踪当前状态的索引。以下是几种常见方法:

方法一:使用useReducer + 历史记录管理

通过useReducer管理状态,同时保存状态变化的历史记录:

react实现撤销恢复

function useUndoReducer(reducer, initialState) {
  const [state, setState] = useState(initialState);
  const [history, setHistory] = useState([initialState]);
  const [index, setIndex] = useState(0);

  function undo() {
    if (index > 0) {
      setIndex(index - 1);
      setState(history[index - 1]);
    }
  }

  function redo() {
    if (index < history.length - 1) {
      setIndex(index + 1);
      setState(history[index + 1]);
    }
  }

  function dispatch(action) {
    const newState = reducer(state, action);
    setHistory([...history.slice(0, index + 1), newState]);
    setIndex(index + 1);
    setState(newState);
  }

  return [state, dispatch, { undo, redo, canUndo: index > 0, canRedo: index < history.length - 1 }];
}

方法二:使用第三方库

Immer和Zustand等库提供了内置的撤销/恢复支持:

import { produceWithPatches, applyPatches } from 'immer';

function useUndoableState(initialState) {
  const [state, setState] = useState(initialState);
  const [history, setHistory] = useState([]);
  const [future, setFuture] = useState([]);

  const updateState = (newState) => {
    const [nextState, patches, inversePatches] = produceWithPatches(state, draft => {
      Object.assign(draft, newState);
    });

    setState(nextState);
    setHistory([...history, inversePatches]);
    setFuture([]);
  };

  const undo = () => {
    if (history.length > 0) {
      const lastPatches = history[history.length - 1];
      const [prevState] = applyPatches(state, lastPatches);

      setState(prevState);
      setHistory(history.slice(0, -1));
      setFuture([lastPatches, ...future]);
    }
  };

  const redo = () => {
    if (future.length > 0) {
      const [nextPatches] = future;
      const [nextState] = applyPatches(state, nextPatches);

      setState(nextState);
      setHistory([...history, nextPatches]);
      setFuture(future.slice(1));
    }
  };

  return [state, updateState, { undo, redo }];
}

方法三:基于命令模式实现

对于复杂应用,可以采用命令模式封装每个操作:

react实现撤销恢复

class Command {
  constructor(execute, undo, value) {
    this.execute = execute;
    this.undo = undo;
    this.value = value;
  }
}

function useCommandStack() {
  const [stack, setStack] = useState([]);
  const [pointer, setPointer] = useState(-1);

  const execute = (command) => {
    command.execute();
    setStack([...stack.slice(0, pointer + 1), command]);
    setPointer(pointer + 1);
  };

  const undo = () => {
    if (pointer >= 0) {
      stack[pointer].undo();
      setPointer(pointer - 1);
    }
  };

  const redo = () => {
    if (pointer < stack.length - 1) {
      stack[pointer + 1].execute();
      setPointer(pointer + 1);
    }
  };

  return { execute, undo, redo };
}

性能优化建议

对于大型状态对象,考虑以下优化策略:

  • 使用差异记录而非完整状态快照
  • 限制历史记录的最大长度
  • 实现节流机制防止高频操作产生过多历史记录
  • 对于不可变数据使用结构共享

实际应用示例

以下是表单编辑场景的撤销/恢复实现:

function FormEditor() {
  const [formData, setFormData, { undo, redo, canUndo, canRedo }] = useUndoableState({
    name: '',
    email: '',
    age: ''
  });

  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData({ ...formData, [name]: value });
  };

  return (
    <div>
      <input name="name" value={formData.name} onChange={handleChange} />
      <input name="email" value={formData.email} onChange={handleChange} />
      <input name="age" value={formData.age} onChange={handleChange} />

      <button onClick={undo} disabled={!canUndo}>Undo</button>
      <button onClick={redo} disabled={!canRedo}>Redo</button>
    </div>
  );
}

标签: react
分享给朋友:

相关文章

如何学react

如何学react

学习 React 的基础知识 React 是一个用于构建用户界面的 JavaScript 库。学习 React 的第一步是掌握其核心概念,包括组件、状态(State)、属性(Props)和生命周期方法…

react如何销毁

react如何销毁

销毁 React 组件 在 React 中,销毁组件通常涉及清理副作用(如事件监听器、定时器或订阅)以防止内存泄漏。以下是实现组件销毁的常见方法: 使用 useEffect 清理副作用 在函数组件…

react实现vue

react实现vue

React 实现 Vue 功能 React 和 Vue 是两种不同的前端框架,但可以通过一些方法在 React 中实现 Vue 的特性。以下是几种常见 Vue 功能在 React 中的实现方式: 双…

react如何更新

react如何更新

更新 React 版本的方法 检查当前 React 版本 运行以下命令查看项目中安装的 React 版本: npm list react 更新 React 及相关依赖 通过 npm 或 yarn…

react如何部署

react如何部署

部署 React 应用的常见方法 使用静态服务器部署 React 应用在构建后会生成静态文件,可以直接通过静态服务器部署。常用的静态服务器包括 Nginx、Apache 等。 运行构建命令生成静态文…

react如何通信

react如何通信

React 组件通信方式 React 组件间的通信方式多样,具体选择取决于组件关系和场景需求。以下是常见方法: 父子组件通信 父组件通过 props 向子组件传递数据,子组件通过回调函数通知父组件…