表单的实时保存(断网、离开页面前保存等情况)最近在写复杂表单的实时保存,一般来说可以使用onblur,但是这可能会存在最
最近在写复杂表单的实时保存,一般来说可以使用onblur,但是这可能会存在最后一个没保存上,以及如果需求是想做到语雀那样的实时保存的情况就不行了。这种情况下我们用了onchange写缓存加定时器调用接口保存。就是有几个极限的情况需要考虑,比如快速填写之后刷新、离开页面、断网、快速提交等。
1、定义一个map缓存(也可以直接用localStorage),在onchange的时候去写缓存。
onValuesChange: async (record) => {
const tempId = `${record.cacheId}-${record.id}`;
const collectValue = [];
for (let i = 0; i < timeArr.length; i++) {
collectValue.push(record[timeArr[i]] ?? '');
}
cacheMap.current.set(tempId, {
...record,
collectValue: JSON.stringify(collectValue),
});
}
2、useEffect中写一个定时器轮询读取缓存,useEffect的return里可以写一些逻辑,在离开页面之前也做一些保存操作,防止定时器还没到时间去保存的情况。在保存成功之后,需要对比缓存中是否还有该条数据,以及是否做了新的更新,如果对象完全相同,删除缓存。
const handleBeforeUnload = async () => {
if (cacheMap.current.size != 0) {
//没保存上的缓存到localstorage中
localStorage.setItem(
`cacheMap${basicInfo?.id}`,
JSON.stringify(Array.from(cacheMap.current.entries())),
);
}
};
const onSave = async (record: CollectIQCListType) => {
const res = await submitCollectRowData({ ...record });
if (res.resp_code === 0) {
const key = `${record.cacheId}-${record.id}`;
const flag = cacheMap.current.has(key);
if (flag && isObjectEqual(record, cacheMap.current.get(key))) {
cacheMap.current.delete(key);
}
}
};
useEffect(() => {
window.addEventListener('beforeunload', handleBeforeUnload);
const intervalId = setInterval(() => {
if (cacheMap.current.size > 0) {
cacheMap.current.forEach((value) => {
if (value) {
onSave(value); //调用保存的接口
}
});
}
}, 1000);
//卸载
return () => {
clearInterval(intervalId);
handleBeforeUnload();
window.removeEventListener('beforeunload', handleBeforeUnload);
};
}, [timeArr]);
3、在进入页面之前检查localStorage里有没有还没保存上的缓存数据。还有提交之前也要检查缓存是否已经清空,在清空缓存之后再去显示页面/提交数据。
const localKey = `cacheMap${basicInfo?.id}`;
const cacheStorage = localStorage.getItem(localKey);
if (cacheStorage && cacheStorage != '[]') {
//因为填写的数据里会有转义字符 \t \n等等 用JSON.parse转会直接报错,用eval又有XSS风险…不知道如何取舍。
const data = eval(cacheStorage?.replaceAll(/\n/g, '\\n'));
const newMap = new Map(data);
newMap.forEach((value) => {
if (value) {
onSave(value);
}
});
localStorage.removeItem(localKey);
}
判断两个对象是否完全相同封装了一个函数:
export const isObjectEqual = (obj1: Record<string, any>, obj2: Record<string, any>) => {
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) {
return false;
}
for (const key of keys1) {
if (!obj2.hasOwnProperty(key) || obj1[key] !== obj2[key]) {
return false;
}
}
return true;
};
像语雀那样的缓存是如何做到的呢?是直接读写localStorage吗?直接多次频繁读写localStorage是否会有性能问题?还有挺多需要考虑的问题。
转载自:https://juejin.cn/post/7373586351625715751