likes
comments
collection
share

表单的实时保存(断网、离开页面前保存等情况)最近在写复杂表单的实时保存,一般来说可以使用onblur,但是这可能会存在最

作者站长头像
站长
· 阅读数 51

最近在写复杂表单的实时保存,一般来说可以使用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
评论
请登录