撤销重做功能广泛用于浏览器的页面切换。
在我的项目中也有一个撤销操作,还有重做操作。
实现原理:
使用两个栈,一个为历史记录栈,一个为未来操作栈。
1)当用户输入的时候,历史记录栈插入当前的值,未来操作栈清空
2)点击撤销( < )的时候,历史记录栈出栈,未来操作栈入栈, past栈顶的元素为当前值
3)点击重做( > )的时候,未来操作栈出栈,历史记录栈入栈,past栈顶的元素为当前值
比如,当用户从""输入了“abc”的时候,栈里面的内容
present="abc"
past = ["a", "ab", "abc"]
future = []
当用户全部删除的时候:
present=""
past=[]
future=["abc", "ab", "a"]
第三方库: redux-undo
安装: npm install redux-undo --save
在原先的reducer的index.ts文件里面修改配置
import { configureStore } from "@reduxjs/toolkit";
import userReducer, { UserStateType } from "./userReducer";
import componentsReducer, { ComponentStateType } from "./componentsReducer";
import pageInfoReducer, { PageInfoType } from "./pageInfoReducer";
import undoable, { excludeAction, StateWithHistory } from "redux-undo";
export type StateType = {
user: UserStateType;
// component: ComponentStateType;
component: StateWithHistory<ComponentStateType>; // add undo
pageInfo: PageInfoType;
};
export default configureStore({
reducer: {
// store current login user information
user: userReducer,
// store component list info
// component: componentsReducer,
component: undoable(componentsReducer, {
limit: 20, // upper limit: 20
filter: excludeAction([
"component/resetComponents",
"component/changeSelectedId",
"component/selectPreviousComponent",
"component/selectNextComponent",
]),
}),
// store page info
pageInfo: pageInfoReducer,
},
});
可以看到: 修改了两处配置。第一处是StateType里面component改为了StateWithHistory<ComponentStateType>.
加了StateWithHistory之后,component就拥有了三个属性 .present, .future .past。和上面的原理一样。
第二处修改是在configureStore里面,
component套上了undoable()。然后下面的limit表示可以后退的次数,filter表示撤销不包含的action
然后在获取这些数据的地方 (在我的项目中是useGetComponentInfo.ts),相比于之前,要加上.present。因为是需要当前的最新的值。
const components = useSelector<StateType>(
(state) => state.component.present
) as ComponentStateType;
实现点击redo / undo的地方:
import { ActionCreators as UndoActionCreators } from "redux-undo";
// undo
const handleUndo = () => {
dispatch(UndoActionCreators.undo());
};
// redo
const handleRedo = () => {
dispatch(UndoActionCreators.redo());
};
然后,我打算实现一个功能:打算在项目中 根据是否可以redo/undo来 禁用对应的按钮
但是遇到了一个bug:
这个bug是: 明明我在filter里面禁用了把所有数据初始化这个dispatch的行为,意味着所有数据初始化之后才是第1步。但是刚加入页面的时候,加载所有服务端传过来的数据之后,还是可以点击撤销,然后回到全部清空的状态。
我debug了一阵子,有past,future的数组,我各自打印了他们的值。发现初始化数据发生在第一步,并没有被禁用掉。所以我怀疑可能是因为数据是异步加载的但是redux-undo不知道,所以加到了history里面。
所以我在初始化数据之后把历史都清空之后解决了这个问题。
dispatch(UndoActionCreators.clearHistory());
Comments
Post a Comment