背景:
最近在项目中遇到一个有意思的需求,大致如下:
如上图红框所示,该列表数据展示的依赖条件总共有四个:两个下拉选择框,一个搜索框和一个分页器。
现有如下需求:
当用户选择或填写了上述条件时,帮助用户记住所操作的内容,当用户再次进入该页面时,保持和离开之前所看到的内容相同。
需求分析:
有了需求之后,我们先对这两个小需求进行简单的分析。
对于该需求,关键词在于“记住”。毫无疑问,肯定是将搜索条件的值存放在localStorage/sessionStorage,需求上应该没什么问题。
技术设计:
需求分析了个七七八八之后,接下来就是技术设计环节。
我们需要将useState中的initialState同步到Storage中去,且在setState的时候对Storage进行同步更新。当读取state时,如果Storage中有值则直接读取Storage中的值,如果没有则读取默认的initialState。
具体api设计:
const [ xxxstate,setXxxState]=useStorageState("存储在Storage中的key值",initialState)
复制代码
技术实现:
import { useState } from 'react';
interface Options {
priority?: 'local' | 'initialValue';
type?: 'localStorage' | 'sessionStorage';
}
//保持状态一致
const valueToStorageFun = <T>({ type, key, value }: { type: Options['type']; key: string; value: T }) => {
if (typeof window !== 'undefined') {
switch (type) {
case 'localStorage':
window.localStorage.setItem(key, JSON.stringify(value));
break;
case 'sessionStorage':
window.sessionStorage.setItem(key, JSON.stringify(value));
break;
default:
window.localStorage.setItem(key, JSON.stringify(value));
}
}
};
export function useStorageState<T>(key: string, initialValue: T, options?: Options) {
const { priority = 'local', type = 'localStorage' } = { priority: 'local', type: 'localStorage', ...options };
const [storedValue, setStoredValue] = useState<T>(() => {
if (typeof window === 'undefined') {
return initialValue;
}
try {
// 根据类型判断从何处读取数据
let item;
switch (type) {
case 'localStorage':
item = window.localStorage.getItem(key);
break;
case 'sessionStorage':
item = window.sessionStorage.getItem(key);
break;
default:
item = window.localStorage.getItem(key);
}
// 解析state并按优先级进行处理
if (item) {
switch (priority) {
case 'local':
return JSON.parse(item);
case 'initialValue':
valueToStorageFun({ key, type, value: initialValue });
return initialValue;
default:
return JSON.parse(item);
}
} else {
return initialValue;
}
} catch (error) {
console.error(error);
return initialValue;
}
});
const setValue = (value: T | ((val: T) => T)) => {
try {
//和useState保持相同用法 支持函数和默认值
const valueToStore = value instanceof Function ? value(storedValue) : value;
//保存state
setStoredValue(valueToStore);
//同步到storage中
valueToStorageFun({ key, type, value: valueToStore });
} catch (error) {
console.log(error);
}
};
return [storedValue, setValue] as const;
}
复制代码
总结:
这里肯定有小伙伴要问了,你写的这个hook跟ahooks中的useLocalStorageState有什么区别呀,不是一样的吗?
眼尖的肯定发现了手动实现useStorageState的参数中有个priority字段,这里是根据业务来决定的。拿上面的业务场景举个简单的例子?:
有一些用户操作记录是需要实时同步到URL中的,比如输入框中的keyword,这个时候当其他页面带着keyword跳到该页面的时候,是不能读本地存储的,需要优先读取URL中的参数,这个时候需要将 priority 设为 initialValue。
这里可能有些小伙伴又要问了,你为什么不直接把这些状态全部同步到URL中呢,还存在缓存中这么麻烦?
为什么没有采用这种方式是因为你在该页面的操作记录,是很难传到其他页面的,其他页面往该页面跳转的时候,你是很难知道这个页面之前的状态。
大致就是这些,若有觉得不妥的地方,请大家积极指点?。