React Hooks -- useLocalStorage

1,593 阅读2分钟

最近在项目中做一个交互优化:需要记录用户最近搜索的关键字

Screen Shot 2021-10-25 at 9.55.34 PM.png

如何封装一个 localStorage hooks,方便后期调用呢?

import React from 'react';

const useStateWithLocalStorage = (key, initVal) => {
  const localStr = localStorage.getItem(key);
  const [value, setValue] = React.useState(localStr || initVal);

  React.useEffect(() => {
    localStorage.setItem(key, value);
  }, [value]);

  return [value, setValue];
};

export default useStateWithLocalStorage;

上面几行代码,其实就可以满足基本的需求了。但是 initVal 只能支持字符串,如果想要使用数组或对象就会有麻烦了。

原因在于 localStorage 中的键值对总是以字符串的形式存储。 需要注意, 和js对象相比, 键值对总是以字符串的形式存储,意味着数值类型会自动转化为字符串类型。

也就是说如果 initVal 如果是对象,在存储时会被转化为 [Object object],这个过程叫做序列化。因此在取值时需要使用反序列化将字符串转换为原来的数据格式。

const useStateWithLocalStorage = (key, initVal) => {
  let preStr;
  try {
    let localStr = localStorage.getItem(key);
    preStr = JSON.parse(localStr as string); // 取值时反序列化
  } catch (error) {
    console.error('localStorage:>> ', error);
  }

  let [value, setValue] = React.useState(preStr || initVal);

  React.useEffect(() => {
    localStorage.setItem(String(key), JSON.stringify(value)); // 存储时序列化
  }, [value]);

  return [value, setValue];
};

export default useStateWithLocalStorage;

以上就可以支持数组、对象等数据格式了,但是要注意的是,key 必须是 string,否则会出现键值对覆盖的情况。因此需要检测 key 的类型,并及时抛错。

const useStateWithLocalStorage = (key, initVal) => {
  if (typeof key !== 'string') {
    throw new Error('key must be a string');
  }

  let preStr;
  ...
};

export default useStateWithLocalStorage;

以上,就是对 useLocalStorage 的封装过程,附上完成代码:

import React from 'react';

/**
 * localStorage 持久化数据
 *
 * @param {*} key
 * @param {*} initVal 初始值,支持数组,对象
 * @return {*}
 */
const useStateWithLocalStorage = (key, initVal) => {
  if (typeof key !== 'string') {
    throw new Error('key must be a string');
  }

  let preStr;
  try {
    let localStr = localStorage.getItem(key);
    if (localStr === undefined) {
      localStr = null; // 避免解析时报错,SyntaxError
    }
    preStr = JSON.parse(localStr as string); // 反序列化
  } catch (error) {
    console.error('useStateWithLocalStorage :>> ', error);
  }

  let [value, setValue] = React.useState(preStr || initVal);

  React.useEffect(() => {
    localStorage.setItem(String(key), JSON.stringify(value)); // 序列化
  }, [value]);

  return [value, setValue];
};

export default useStateWithLocalStorage;

小结

这个场景主要考察了 localStorage 键值对的「隐式」转换,如何解决转换的问题,以及 JSON 解析过程中一些莫名其妙的报错,如:

VM733:1 Uncaught SyntaxError: Unexpected token o in JSON at position 0

localStorage 的使用是有一些小坑,对前端的基本知识也还是有一些考验的。

参考链接