基本原理
- 模拟一个输入框的输入/撤销/重做的过程
- 假设定义变量 present 存储当前输入框的值;定义栈结构数组 past 存储历史数据;定义栈结构数组 future 存储未来数据记录。
present = 'a'
past = ['a']
future = []
present = 'ab'
past = ['a', 'ab']
future = []
present = 'a'
past = ['a']
future = ['ab']
present = ''
past = []
future = ['ab', 'a']
present = 'a'
past = ['a']
future = ['ab']
使用 redux-undo 实现 简易todolist 撤销/重做 样例
import { configureStore } from '@reduxjs/toolkit'
import todoListReducer, { TodoItemType } from './todoList'
import undoable, { StateWithHistory, excludeAction } from 'redux-undo'
export type StateType = {
todoList: StateWithHistory<TodoItemType[]>
}
export default configureStore({
reducer: {
todoList: undoable(todoListReducer, {
limit: 20,
filter: excludeAction(['todoList/toggleComplete']),
})
}
})
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
import { nanoid } from "nanoid";
export type TodoItemType = {
id: string,
title: string,
complete: boolean
}
const INIT_STATE: TodoItemType[] = [
{
id: nanoid(5),
title: 'test',
complete: false
},
{
id: nanoid(5),
title: 'test1',
complete: true
}
]
const todoListSlice = createSlice({
name: 'todoList',
initialState: INIT_STATE,
reducers: {
addTodo(state: TodoItemType[], action: PayloadAction<TodoItemType>){
return [action.payload, ...state]
},
removeTodo(state: TodoItemType[], action: PayloadAction< { id: string } >){
return state.filter(todo => todo.id !== action.payload.id)
},
toggleComplete(state: TodoItemType[], action: PayloadAction< { id: string } >){
const {id: toggleId} = action.payload
return state.map(todo => {
const {id, complete} = todo
if(id !== toggleId) return todo
return {
...todo,
complete: !complete
}
})
},
}
})
export const {addTodo, removeTodo, toggleComplete} = todoListSlice.actions
export default todoListSlice.reducer
import React, {FC} from "react";
import { useSelector, useDispatch } from "react-redux";
import { StateType } from "../store/index";
import { TodoItemType, addTodo, removeTodo, toggleComplete } from "../store/todoList";
import { nanoid } from "nanoid";
import { ActionCreators } from "redux-undo";
const TodoList: FC = () => {
const todoList = useSelector<StateType>(state => state.todoList.present) as TodoItemType[]
const dispatch = useDispatch()
function add(){
const id = nanoid(5)
const data = {
id,
title: `title-${id}`,
complete: false
}
dispatch(addTodo(data))
}
function del(id: string) {
dispatch(removeTodo({id}))
}
function toggle(id: string) {
dispatch(toggleComplete({id}))
}
function undo() {
dispatch(ActionCreators.undo())
}
function redo() {
dispatch(ActionCreators.redo())
}
return (
<>
<button onClick={add}>添加</button>
<ul>
{todoList.map(todo => {
const {id, title, complete} = todo
return (
<li key={id} style={{textDecoration: complete ? 'line-through' : ''}}>
<span onClick={() => { toggle(id) }}>{title}</span>
<button onClick={() => { del(id) }}>删除</button>
</li>
)
})}
</ul>
<div>
<button onClick={undo}>undo</button>
<button onClick={redo}>redo</button>
</div>
</>
)
}
export default TodoList