useLocalStorageState & useSessionStorageState
简介
这两个hooks就是将 state 进行持久化存储在 localStorage/sessionStorage 中,我们仅需要查看一个hooks的用法即可。
useLocalStorageState demo 🔗
源码实现
useLocalStorageState
其源码文件 useLocalStorageState/index.ts 如下
import { createUseStorageState } from "../createUseStorageState";
import isBrowser from "../utils/isBrowser";
const useLocalStorageState = createUseStorageState(() =>
isBrowser ? localStorage : undefined
);
export default useLocalStorageState;
useSessionStorageState
其源码文件 useSessionStorageState/index.ts 如下
import { createUseStorageState } from "../createUseStorageState";
import isBrowser from "../utils/isBrowser";
const useSessionStorageState = createUseStorageState(() =>
isBrowser ? sessionStorage : undefined
);
export default useSessionStorageState;
如果当前使用环境为浏览器的话,我才能使用这两个hooks
const isBrowser = !!(
typeof window !== 'undefined' &&
window.document &&
window.document.createElement
);
export default isBrowser;
发现这两个 hooks 的实现都是基于 createUseStorageState,因此我们只需要研读该方法的实现即可 如果阅读源码期间遇到别的 hooks (useMemoizedFn, useUpdateEffect),请参考对应文档详解,这里仅提供简单说明
createUseStorageState
useSessionStorageState/index.ts
// 为了简化内容,去掉了部分Typescript定义
export function createUseStorageState(getStorage: () => Storage | undefined) {
/***
* useStorageState 传递参数
* key:持久化存储的key,必须
* options:{ // 可选
* defaultValue: , 默认值,可选
* serializer: (v) => v, 序列化函数,存值时候使用,可选
* deserializer: (v) => v, 反序列化函数,取值时候使用,可选
* }
*/
function useStorageState<T>(key: string, options?: Options<T> & OptionsWithDefaultValue<T>) {
let storage: Storage | undefined; // storage的实例,localStorage | sessionStorage
// https://github.com/alibaba/hooks/issues/800
// 上述链接是关于,如果浏览器禁用cookies,那么该hooks将会报错,因为禁用了cookies,浏览器会提示 "SecurityError: Failed to read the 'localStorage' property from 'Window': Access is denied for this document." 即禁止该网站使用浏览器存储数据,以下try catch就是关于这个issue的修复代码
try {
storage = getStorage();
} catch (err) {
console.error(err);
}
// 序列化函数,用户可以传递自己的序列化函数
// 默认是 JSON.stringify
const serializer = (value: T) => {
if (options?.serializer) {
return options?.serializer(value);
}
return JSON.stringify(value);
};
// 反序列化函数,用户可以传递自己的反序列化函数
// 默认是 JSON.parse
const deserializer = (value: string) => {
if (options?.deserializer) {
return options?.deserializer(value);
}
return JSON.parse(value);
};
// 在取数据的时候,先查看本地存储中是否有该key的数据
// 如果存在则使用反序列化函数进行解析后返回
// 如果不存在,则返回用户提供的默认值
// 如果该默认值是函数,则执行该函数,将结果返回
function getStoredValue() {
try {
const raw = storage?.getItem(key);
if (raw) {
return deserializer(raw);
}
} catch (e) {
console.error(e);
}
if (isFunction(options?.defaultValue)) {
return options?.defaultValue();
}
return options?.defaultValue;
}
const [state, setState] = useState<T | undefined>(() => getStoredValue());
// useUpdateEffect: 使用上与 useEffect 完全相同,只是它忽略了首次执行,只在依赖项更新时执行。
useUpdateEffect(() => {
setState(getStoredValue());
}, [key]);
// 更新操作
// 如果用户在赋值函数中传入undefined值,则默认清空本地存储中的该 item
// 如果用户传入一个方法,则将老值当作参数,调用改方法后得到新值,将新值序列化之后进行吃持久化存储
// 如果是其他值,则直接更新值,再将新值序列化之后进行吃持久化存储
const updateState = (value?: T | IFuncUpdater<T>) => {
if (typeof value === 'undefined') {
setState(undefined);
storage?.removeItem(key);
} else if (isFunction(value)) {
const currentState = value(state);
try {
setState(currentState);
storage?.setItem(key, serializer(currentState));
} catch (e) {
console.error(e);
}
} else {
try {
setState(value);
storage?.setItem(key, serializer(value));
} catch (e) {
console.error(e);
}
}
};
// 返回值和更新函数
// useMemoizedFn: 持久化 function 的 Hook,理论上,可以使用 useMemoizedFn 完全代替 useCallback。
return [state, useMemoizedFn(updateState)];
}
return useStorageState;
}