This article is the ninth in a series of in-depth ahooks source code series, which has been organized into document- address . I think it's not bad, give me a follow to support it, thanks.

Today, let's see how ahooks encapsulates cookie/localStorage/sessionStorage.

cookies

ahooks encapsulates useCookieState, a Hook that stores state in cookies.

The hook uses the js-cookie npm library. I think the reasons for choosing it are as follows:

  • The package is small in size. Less than 800 bytes after compression. itself has no other dependencies. This is important for ahooks that are originally a tool library.
  • better compatibility. All browsers are supported. And supports arbitrary characters.

Of course, it also has other features, such as support for ESM/AMD/CommonJS import and so on.

The encapsulated code is not complicated. Let’s first look at the default value settings. The priorities are as follows:

  • If the value already exists in the local cookie, take it directly.
  • If the set value is a string, it will be returned directly.
  • Set the value to a function, execute the function, and return the function execution result.
  • Returns the defaultValue set in options.
 const [state, setState] = useState<State>(() => {
  // 假如有值,则直接返回
  const cookieValue = Cookies.get(cookieKey);

  if (isString(cookieValue)) return cookieValue;
  // 定义 Cookie 默认值,但不同步到本地 Cookie
  // 可以自定义默认值
  if (isFunction(options.defaultValue)) {
    return options.defaultValue();
  }

  return options.defaultValue;
});

Let's look at the logic of setting a cookie -- updateState method.

  • When using the updateState method, developers can pass in new options -- newOptions. The merge operation will be performed with the options set by useCookieState. Finally, in addition to defaultValue, it will be transparently passed to the third parameter of the set method of js-cookie.
  • Get the value of the cookie, judge the incoming value, if it is a function, take the result returned after execution, otherwise take the value directly.
  • If the value is undefined, the cookie is cleared. Otherwise, call the set method of js-cookie.
  • Finally, the value of the cookie and the method of setting it are returned.
 // 设置 Cookie 值
const updateState = useMemoizedFn(
  (
    newValue: State | ((prevState: State) => State),
    newOptions: Cookies.CookieAttributes = {},
  ) => {
    const { defaultValue, ...restOptions } = { ...options, ...newOptions };
    setState((prevState) => {
      const value = isFunction(newValue) ? newValue(prevState) : newValue;
      // 值为 undefined 的时候,清除 cookie
      if (value === undefined) {
        Cookies.remove(cookieKey);
      } else {
        Cookies.set(cookieKey, value, restOptions);
      }
      return value;
    });
  },
);

return [state, updateState] as const;

localStorage/sessionStorage

ahooks encapsulates useLocalStorageState and useSessionStorageState. Hooks that store state in localStorage and sessionStorage.

The usage of the two is the same, because the official use the same method to encapsulate. Let's take useLocalStorageState as an example.

You can see that useLocalStorageState is actually the result returned by calling the createUseStorageState method. The participants of this method judge whether it is a browser environment to decide whether to use localStorage, because ahooks needs to support server-side rendering.

 import { createUseStorageState } from '../createUseStorageState';
import isBrowser from '../utils/isBrowser';

const useLocalStorageState = createUseStorageState(() => (isBrowser ? localStorage : undefined));

export default useLocalStorageState;

Let's focus on the createUseStorageState method.

  • The first is to call the incoming parameters. If an error is reported, it will be caught in time. This is because:

    • It can be seen that the storage returned here may actually be undefined, and there will be catch processing later.
    • In addition, it can be seen from this issue that when cookies are disabled, localStorage cannot be accessed. stackoverflow also has this discussion. (Strange knowledge added again)
 export function createUseStorageState(getStorage: () => Storage | undefined) {
  function useStorageState<T>(key: string, options?: Options<T>) {
    let storage: Storage | undefined;
    // https://github.com/alibaba/hooks/issues/800
    try {
      storage = getStorage();
    } catch (err) {
      console.error(err);
    }
    // 代码在后面讲解
}
  • Support for custom serialization methods. If not, directly JSON.stringify.
  • Support for custom deserialization methods. If not, directly JSON.parse.
  • getStoredValue Get the default value of storage, if there is no local value, return the default value.
  • When the incoming key is updated, reassign it.
 // 自定义序列化方法
const serializer = (value: T) => {
  if (options?.serializer) {
    return options?.serializer(value);
  }
  return JSON.stringify(value);
};

// 自定义反序列化方法
const deserializer = (value: string) => {
  if (options?.deserializer) {
    return options?.deserializer(value);
  }
  return JSON.parse(value);
};

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());

// 当 key 更新的时候执行
useUpdateEffect(() => {
  setState(getStoredValue());
}, [key]);

And finally the function to update storage:

  • If the value is undefined, removeItem, remove the storage.
  • If it is a function, take the result after execution.
  • Otherwise, take the value directly.
 // 设置 State
const updateState = (value?: T | IFuncUpdater<T>) => {
  // 如果是 undefined,则移除选项
  if (isUndef(value)) {
    setState(undefined);
    storage?.removeItem(key);
    // 如果是function,则用来传入 state,并返回结果
  } 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);
    }
  }
};

Summarize and summarize

The encapsulation of cookie/localStorage/sessionStorage is what we often need to do. The overall encapsulation of ahooks is relatively simple, and you can refer to it for reference.

This article has been included in the personal blog , welcome to pay attention~


Gopal
366 声望77 粉丝