likes
comments
collection
share

彻底搞懂ahooks State实现原理前言 声明:本文所有代码块均来自ahooks 3.8.1版本。 上一篇我们详细了

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

前言

声明:本文所有代码块均来自ahooks 3.8.1版本。

上一篇我们详细了解了ahooks的所有副作用Effect钩子:

今天来讲解所有的状态State Hook

useSetState

管理 object 类型 state 的 Hooks,用法与 class 组件的 this.setState 基本一致。

源码如下:

import { useCallback, useState } from 'react';
import { isFunction } from '../utils';

export type SetState<S extends Record<string, any>> = <K extends keyof S>(
  state: Pick<S, K> | null | ((prevState: Readonly<S>) => Pick<S, K> | S | null),
) => void;

const useSetState = <S extends Record<string, any>>(
  initialState: S | (() => S),
): [S, SetState<S>] => {
  const [state, setState] = useState<S>(initialState);

  const setMergeState = useCallback((patch) => {
    setState((prevState) => {
      const newState = isFunction(patch) ? patch(prevState) : patch;
      return newState ? { ...prevState, ...newState } : prevState;
    });
  }, []);

  return [state, setMergeState];
};

export default useSetState;

源码不复杂,入参和出参与React自带的useState一致,接收一个初始状态initialState,可以是一个状态对象或者是返回状态对象的函数,同时定制化了set函数,声明了一个setMergeState函数。

  • 使用useCallback创建了一个记忆化函数,确保当依赖项没有变化时不重新创建;
  • 接收patch参数,表示要合并到当前状态的部分内容,这个参数可以是状态部分对象;
  • 如果patch是一个函数,则会执行这个函数并获取新状态;
  • 最后在合并状态时会将老状态和新状态合并,如果新状态是null,则返回老状态;

useToggle

用于在两个状态值之间切换的 Hook。

用法如下:

const [state, { toggle, setLeft, setRight }] = useToggle('Hello', 'World');

源码如下:

import { useMemo, useState } from 'react';

export interface Actions<T> {
  setLeft: () => void;
  setRight: () => void;
  set: (value: T) => void;
  toggle: () => void;
}

function useToggle<T = boolean>(): [boolean, Actions<T>];

function useToggle<T>(defaultValue: T): [T, Actions<T>];

function useToggle<T, U>(defaultValue: T, reverseValue: U): [T | U, Actions<T | U>];

function useToggle<D, R>(defaultValue: D = false as unknown as D, reverseValue?: R) {
  const [state, setState] = useState<D | R>(defaultValue);

  const actions = useMemo(() => {
    const reverseValueOrigin = (reverseValue === undefined ? !defaultValue : reverseValue) as D | R;

    const toggle = () => setState((s) => (s === defaultValue ? reverseValueOrigin : defaultValue));
    const set = (value: D | R) => setState(value);
    const setLeft = () => setState(defaultValue);
    const setRight = () => setState(reverseValueOrigin);

    return {
      toggle,
      set,
      setLeft,
      setRight,
    };
    // useToggle ignore value change
    // }, [defaultValue, reverseValue]);
  }, []);

  return [state, actions];
}

export default useToggle;

1. 接口定义

export interface Actions<T> {
  setLeft: () => void; // 设置状态为默认值
  setRight: () => void; // 设置状态为反转值
  set: (value: T) => void; // 设置状态为指定值
  toggle: () => void; // 切换状态
}

这个接口定义了 Actions 类型,它包含了四个函数来操作状态。

2. useToggle 函数重载

function useToggle<T = boolean>(): [boolean, Actions<T>];
function useToggle<T>(defaultValue: T): [T, Actions<T>];
function useToggle<T, U>(defaultValue: T, reverseValue: U): [T | U, Actions<T | U>];

这里使用了 TypeScript 的重载功能,定义了三个不同的 useToggle 函数签名,以支持不同的用法。可以:

  • 不传参数,默认值为 false
  • 传入一个默认值。
  • 传入默认值和一个反转值。

3. 实现

function useToggle<D, R>(defaultValue: D = false as unknown as D, reverseValue?: R) {
  const [state, setState] = useState<D | R>(defaultValue);

这里可以看到,useState 被用来管理当前的状态,状态初始化为 defaultValue

4. 操作函数

const actions = useMemo(() => {
    const reverseValueOrigin = (reverseValue === undefined ? !defaultValue : reverseValue) as D | R;
    
    const toggle = () => setState((s) => (s === defaultValue ? reverseValueOrigin : defaultValue));
    const set = (value: D | R) => setState(value);
    const setLeft = () => setState(defaultValue);
    const setRight = () => setState(reverseValueOrigin);
    
    return {
      toggle,
      set,
      setLeft,
      setRight,
    };
}, []);

在这个 useMemo 中,定义了四个操作函数:

  • toggle:切换状态,如果当前状态是 defaultValue,则设置为 reverseValueOrigin,反之则设置为 defaultValue
  • set:直接设置状态为给定值。
  • setLeft:设置状态为 defaultValue
  • setRight:设置状态为 reverseValueOrigin

5. 返回值

return [state, actions];

最后,返回当前状态和操作对象。

useBoolean

ahooks专门用于优雅地管理booleanhook

useBooleanuseToggle非常类似,在实现上也是直接将useBoolean的核心逻辑托管给了 useToggle

基本用法如下:

const [state, { toggle, setTrue, setFalse }] = useBoolean(true);

源码实现如下:

import { useMemo } from 'react';
import useToggle from '../useToggle';

export interface Actions {
  setTrue: () => void;
  setFalse: () => void;
  set: (value: boolean) => void;
  toggle: () => void;
}

export default function useBoolean(defaultValue = false): [boolean, Actions] {
  const [state, { toggle, set }] = useToggle(!!defaultValue);

  const actions: Actions = useMemo(() => {
    const setTrue = () => set(true);
    const setFalse = () => set(false);
    return {
      toggle,
      set: (v) => set(!!v),
      setTrue,
      setFalse,
    };
  }, []);

  return [state, actions];
}

暴露出去的函数和useToggle完全一致,只是把值固定成了boolean类型。

useCookieState

useCookieState用于管理 cookie 的状态。它结合了 React 的 state 和 js-cookie库来读取和写入 cookies。

源码如下:

import Cookies from 'js-cookie';
import { useState } from 'react';
import useMemoizedFn from '../useMemoizedFn';
import { isFunction, isString } from '../utils';

export type State = string | undefined;

export interface Options extends Cookies.CookieAttributes {
  defaultValue?: State | (() => State);
}

function useCookieState(cookieKey: string, options: Options = {}) {
  const [state, setState] = useState<State>(() => {
    const cookieValue = Cookies.get(cookieKey);

    if (isString(cookieValue)) return cookieValue;

    if (isFunction(options.defaultValue)) {
      return options.defaultValue();
    }

    return options.defaultValue;
  });

  const updateState = useMemoizedFn(
    (
      newValue: State | ((prevState: State) => State),
      newOptions: Cookies.CookieAttributes = {},
    ) => {
      const { defaultValue, ...restOptions } = { ...options, ...newOptions };
      const value = isFunction(newValue) ? newValue(state) : newValue;

      setState(value);

      if (value === undefined) {
        Cookies.remove(cookieKey);
      } else {
        Cookies.set(cookieKey, value, restOptions);
      }
    },
  );

  return [state, updateState] as const;
}

export default useCookieState;

类型定义

  1. State:

    • 可以是 string 或 undefined。它表示 cookie 的值。
  2. Options:

    • 继承了 Cookies.CookieAttributes,用于配置 cookie 的属性(如过期时间、路径等)。
    • defaultValue:可以是一个初始值或者一个返回初始值的函数。

Hook 的功能

  1. 初始化状态:

    • 使用 useState Hook 初始化状态,通过读取指定 cookie 的值。
    • 如果 cookie 存在且为字符串,则使用它的值;如果 cookie 不存在并且 defaultValue 是一个函数,则调用这个函数得到值;如果 defaultValue 不是函数,则直接使用 defaultValue 指定的值。
  2. 更新状态:

    • 提供一个 updateState 函数,用于更新 cookie 和内部状态。
    • newValue 可以是新值或一个返回新值的函数(接受前一个状态作为参数)。
    • 合并 newOptions 和 options,然后设置 cookie。若新值是 undefined,则删除指定的 cookie。
  3. 返回值:

    • 返回一个数组,包含当前状态和更新状态的函数。

核心逻辑流程

  • 当组件第一次渲染时,useCookieState 会读取 cookie 的值并设置为状态。
  • 当调用 updateState 时,会根据传入的新值或函数更新状态及 cookie。
  • 如果状态被更新为 undefined,则删除对应的 cookie。

useLocalStorageState

用于同步localStorage数据到React状态中的Hook,源码入口是调用了一个公共函数,如下:

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

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

export default useLocalStorageState;

可以看到,熟悉的模块划分,也不难想象到这里useSessionStorageState一定也通用了这个函数,createUseStorageState源码如下:

import { useState } from 'react';
import useEventListener from '../useEventListener';
import useMemoizedFn from '../useMemoizedFn';
import useUpdateEffect from '../useUpdateEffect';
import { isFunction, isUndef } from '../utils';

export const SYNC_STORAGE_EVENT_NAME = 'AHOOKS_SYNC_STORAGE_EVENT_NAME';

export type SetState<S> = S | ((prevState?: S) => S);

export interface Options<T> {
  defaultValue?: T | (() => T);
  listenStorageChange?: boolean;
  serializer?: (value: T) => string;
  deserializer?: (value: string) => T;
  onError?: (error: unknown) => void;
}

export function createUseStorageState(getStorage: () => Storage | undefined) {
  function useStorageState<T>(key: string, options: Options<T> = {}) {
    let storage: Storage | undefined;
    const {
      listenStorageChange = false,
      onError = (e) => {
        console.error(e);
      },
    } = options;

    // https://github.com/alibaba/hooks/issues/800
    try {
      storage = getStorage();
    } catch (err) {
      onError(err);
    }

    const serializer = (value: T) => {
      if (options.serializer) {
        return options.serializer(value);
      }
      return JSON.stringify(value);
    };

    const deserializer = (value: string): T => {
      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) {
        onError(e);
      }
      if (isFunction(options.defaultValue)) {
        return options.defaultValue();
      }
      return options.defaultValue;
    }

    const [state, setState] = useState(getStoredValue);

    useUpdateEffect(() => {
      setState(getStoredValue());
    }, [key]);

    const updateState = (value?: SetState<T>) => {
      const currentState = isFunction(value) ? value(state) : value;

      if (!listenStorageChange) {
        setState(currentState);
      }

      try {
        let newValue: string | null;
        const oldValue = storage?.getItem(key);

        if (isUndef(currentState)) {
          newValue = null;
          storage?.removeItem(key);
        } else {
          newValue = serializer(currentState);
          storage?.setItem(key, newValue);
        }

        dispatchEvent(
          // send custom event to communicate within same page
          // importantly this should not be a StorageEvent since those cannot
          // be constructed with a non-built-in storage area
          new CustomEvent(SYNC_STORAGE_EVENT_NAME, {
            detail: {
              key,
              newValue,
              oldValue,
              storageArea: storage,
            },
          }),
        );
      } catch (e) {
        onError(e);
      }
    };

    const syncState = (event: StorageEvent) => {
      if (event.key !== key || event.storageArea !== storage) {
        return;
      }

      setState(getStoredValue());
    };

    const syncStateFromCustomEvent = (event: CustomEvent<StorageEvent>) => {
      syncState(event.detail);
    };

    // from another document
    useEventListener('storage', syncState, {
      enable: listenStorageChange,
    });

    // from the same document but different hooks
    useEventListener(SYNC_STORAGE_EVENT_NAME, syncStateFromCustomEvent, {
      enable: listenStorageChange,
    });

    return [state, useMemoizedFn(updateState)] as const;
  }

  return useStorageState;
}

核心原理是自定义了一个ahook storage的更新事件,名为SYNC_STORAGE_EVENT_NAME,接下来我们详细拆解下每个模块

1. 导入模块

import { useState } from 'react';
import useEventListener from '../useEventListener';
import useMemoizedFn from '../useMemoizedFn';
import useUpdateEffect from '../useUpdateEffect';
import { isFunction, isUndef } from '../utils';
  • useState: React 的状态管理 Hook。
  • useEventListener: 自定义 Hook,用于监听事件。
  • useMemoizedFn: 可能是一个优化 Hook,用于避免函数在每次渲染时重新创建。
  • useUpdateEffect: 可能是一个自定义 Hook,仅在组件更新时执行副作用。
  • isFunction 和 isUndef: 工具函数,用于检查一个值是否为函数或未定义。

2. 常量和类型定义

export const SYNC_STORAGE_EVENT_NAME = 'AHOOKS_SYNC_STORAGE_EVENT_NAME';
export type SetState<S> = S | ((prevState?: S) => S);
export interface Options<T> {
  defaultValue?: T | (() => T);
  listenStorageChange?: boolean;
  serializer?: (value: T) => string;
  deserializer?: (value: string) => T;
  onError?: (error: unknown) => void;
}
  • SYNC_STORAGE_EVENT_NAME: 同步存储的事件名称。
  • SetState<S>: 定义状态的类型,可以是一个值或一个接受当前状态的函数。
  • Options<T>: 用于配置 Hook 行为的选项,包含默认值、是否监听存储变化、序列化和反序列化函数,错误处理函数等。

3. 主函数 createUseStorageState

export function createUseStorageState(getStorage: () => Storage | undefined) {
  • 这是一个工厂函数,接受一个参数 getStorage,用于获取存储对象(例如 localStorage 或 sessionStorage)。

4. useStorageState Hook

function useStorageState<T>(key: string, options: Options<T> = {}) {

这是具体的自定义 Hook 函数,实现了对存储状态的管理。

4.1 读取存储对象

let storage: Storage | undefined;
const {
  listenStorageChange = false,
  onError = (e) => {
    console.error(e);
  },
} = options;

try {
  storage = getStorage();
} catch (err) {
  onError(err);
}
  • 尝试获取 storage 对象,并处理可能的错误。

4.2 定义序列化和反序列化函数

const serializer = (value: T) => {
  if (options.serializer) {
    return options.serializer(value);
  }
  return JSON.stringify(value);
};

const deserializer = (value: string): T => {
  if (options.deserializer) {
    return options.deserializer(value);
  }
  return JSON.parse(value);
};
  • 如果传入了自定义的序列化和反序列化函数,将调用这些函数,否则使用默认的 JSON 方法。

4.3 获取存储值

function getStoredValue() {
  try {
    const raw = storage?.getItem(key);
    if (raw) {
      return deserializer(raw);
    }
  } catch (e) {
    onError(e);
  }
  if (isFunction(options.defaultValue)) {
    return options.defaultValue();
  }
  return options.defaultValue;
}
  • 尝试从存储中获取值,并返回反序列化后的结果。如果没有值,则返回默认值。

4.4 状态管理

const [state, setState] = useState(getStoredValue);
  • 使用 useState 创建存储状态。

4.5 更新状态和同步存储

const updateState = (value?: SetState<T>) => {
  const currentState = isFunction(value) ? value(state) : value;
  
  if (!listenStorageChange) {
    setState(currentState);
  }
  
  // 更新 storage
  // 发送自定义事件以同步数据到同一页面的其他组件
};
  • updateState 函数用于更新状态并在 Storage 中同步值。如果启用了存储变化监听,会同步更新。

4.6 监听 Storage 事件

useEventListener('storage', syncState, { enable: listenStorageChange });
useEventListener(SYNC_STORAGE_EVENT_NAME, syncStateFromCustomEvent, { enable: listenStorageChange });
  • 监听来自其他文档的 storage 事件和自定义同步事件。

5. 返回值

return [state, useMemoizedFn(updateState)] as const;
  • 返回当前状态和更新状态的函数,使用 useMemoizedFn 确保函数的引用在依赖未改变时不会变化。

useSessionStorageState

用法与useLocalStorageState完全一致,切换为sessionStorage存储,不再过多讲解。

源码如下:

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

const useSessionStorageState = createUseStorageState(() =>
  isBrowser ? sessionStorage : undefined,
);

export default useSessionStorageState;

useDebounce

上一篇文章介绍过useDebounceFnuseThrottle,基于lodash实现了防抖、节流的通用能力,useDebounce实际就是将能力包了一层,将值暴露出去。

源码如下:

import { useEffect, useState } from 'react';
import useDebounceFn from '../useDebounceFn';
import type { DebounceOptions } from './debounceOptions';

function useDebounce<T>(value: T, options?: DebounceOptions) {
  const [debounced, setDebounced] = useState(value);

  const { run } = useDebounceFn(() => {
    setDebounced(value);
  }, options);

  useEffect(() => {
    run();
  }, [value]);

  return debounced;
}

export default useDebounce;

useThrottle

useDebounce原理一致,不再过多讲解。

源码如下:

import { useEffect, useState } from 'react';
import useThrottleFn from '../useThrottleFn';
import type { ThrottleOptions } from './throttleOptions';

function useThrottle<T>(value: T, options?: ThrottleOptions) {
  const [throttled, setThrottled] = useState(value);

  const { run } = useThrottleFn(() => {
    setThrottled(value);
  }, options);

  useEffect(() => {
    run();
  }, [value]);

  return throttled;
}

export default useThrottle;

useMap

用于管理JavaScript哈希表的Hook,具体用法和实现比较简单,核心思路就是在React状态机制中维护一个哈希表,进行增删改查,源码如下:

import { useState } from 'react';
import useMemoizedFn from '../useMemoizedFn';

function useMap<K, T>(initialValue?: Iterable<readonly [K, T]>) {
  const getInitValue = () => new Map(initialValue);
  const [map, setMap] = useState<Map<K, T>>(getInitValue);

  const set = (key: K, entry: T) => {
    setMap((prev) => {
      const temp = new Map(prev);
      temp.set(key, entry);
      return temp;
    });
  };

  const setAll = (newMap: Iterable<readonly [K, T]>) => {
    setMap(new Map(newMap));
  };

  const remove = (key: K) => {
    setMap((prev) => {
      const temp = new Map(prev);
      temp.delete(key);
      return temp;
    });
  };

  const reset = () => setMap(getInitValue());

  const get = (key: K) => map.get(key);

  return [
    map,
    {
      set: useMemoizedFn(set),
      setAll: useMemoizedFn(setAll),
      remove: useMemoizedFn(remove),
      reset: useMemoizedFn(reset),
      get: useMemoizedFn(get),
    },
  ] as const;
}

export default useMap;

实现流程如下:

1. 引入依赖

import { useState } from 'react';
import useMemoizedFn from '../useMemoizedFn';
  • useState:React 的 Hook,用于在组件内部管理状态。
  • useMemoizedFn:假设这是一个自定义 Hook,用于在依赖不变时返回相同的函数引用,以避免不必要的重新渲染。

2. 定义 useMap 函数

function useMap<K, T>(initialValue?: Iterable<readonly [K, T]>) {
  • 这个函数接受一个可选的 initialValue 参数,它是一个可迭代的键值对集合,默认为 undefined
  • K 和 T 是泛型类型,用于定义 Map 的键和值的类型。

3. 初始化状态

const getInitValue = () => new Map(initialValue);
const [map, setMap] = useState<Map<K, T>>(getInitValue);
  • getInitValue:这是一个函数,它将 initialValue 转换成一个新的 Map 对象。
  • useState 用于创建一个名为 map 的状态,并提供 setMap 方法来更新 map

4. 定义操作方法

设置单个项

const set = (key: K, entry: T) => {
  setMap((prev) => {
    const temp = new Map(prev);
    temp.set(key, entry);
    return temp;
  });
};
  • set 方法通过调用 setMap 更新 map。它复制当前的 map,然后使用 set 方法将新值添加到一个临时 Map 中,并返回这个新的 Map

设置多个项

const setAll = (newMap: Iterable<readonly [K, T]>) => {
  setMap(new Map(newMap));
};
  • setAll 方法将传入的新键值对集合转换成一个 Map 并直接更新 map

删除项

const remove = (key: K) => {
  setMap((prev) => {
    const temp = new Map(prev);
    temp.delete(key);
    return temp;
  });
};
  • remove 方法从当前 map 中删除指定的键。

重置 Map

const reset = () => setMap(getInitValue());
  • reset 方法将 map 重置为初始化时的值。

获取某个值

const get = (key: K) => map.get(key);
  • get 方法通过给定的键返回对应的值。

5. 返回值

return [
  map,
  {
    set: useMemoizedFn(set),
    setAll: useMemoizedFn(setAll),
    remove: useMemoizedFn(remove),
    reset: useMemoizedFn(reset),
    get: useMemoizedFn(get),
  },
] as const;
  • 返回一个包含 map 和操作方法的元组。操作方法使用 useMemoizedFn 包装,以确保这些函数在依赖未变时不会重新创建,从而改善性能及避免不必要的渲染。

useSet

实现原理和思路与useMap类似,也是基于一个React state来维护一个Set,源码如下:

import { useState } from 'react';
import useMemoizedFn from '../useMemoizedFn';

function useSet<K>(initialValue?: Iterable<K>) {
  const getInitValue = () => new Set(initialValue);
  const [set, setSet] = useState<Set<K>>(getInitValue);

  const add = (key: K) => {
    if (set.has(key)) {
      return;
    }
    setSet((prevSet) => {
      const temp = new Set(prevSet);
      temp.add(key);
      return temp;
    });
  };

  const remove = (key: K) => {
    if (!set.has(key)) {
      return;
    }
    setSet((prevSet) => {
      const temp = new Set(prevSet);
      temp.delete(key);
      return temp;
    });
  };

  const reset = () => setSet(getInitValue());

  return [
    set,
    {
      add: useMemoizedFn(add),
      remove: useMemoizedFn(remove),
      reset: useMemoizedFn(reset),
    },
  ] as const;
}

export default useSet;

usePrevious

用于保存上一次状态的Hook,实现思路是通过useRef来留存每一次的老状态。

源码如下:

import { useRef } from 'react';

export type ShouldUpdateFunc<T> = (prev: T | undefined, next: T) => boolean;

const defaultShouldUpdate = <T>(a?: T, b?: T) => !Object.is(a, b);

function usePrevious<T>(
  state: T,
  shouldUpdate: ShouldUpdateFunc<T> = defaultShouldUpdate,
): T | undefined {
  const prevRef = useRef<T>();
  const curRef = useRef<T>();

  if (shouldUpdate(curRef.current, state)) {
    prevRef.current = curRef.current;
    curRef.current = state;
  }

  return prevRef.current;
}

export default usePrevious;

useSafeState

一个性能优化Hook,如果setState更新时机在组件销毁的时候,则不更新,可以避免内存泄漏。

源码如下:

import { useCallback, useState } from 'react';
import type { Dispatch, SetStateAction } from 'react';
import useUnmountedRef from '../useUnmountedRef';

function useSafeState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];

function useSafeState<S = undefined>(): [S | undefined, Dispatch<SetStateAction<S | undefined>>];

function useSafeState<S>(initialState?: S | (() => S)) {
  const unmountedRef = useUnmountedRef();
  const [state, setState] = useState(initialState);
  const setCurrentState = useCallback((currentState) => {
    /** if component is unmounted, stop update */
    if (unmountedRef.current) return;
    setState(currentState);
  }, []);

  return [state, setCurrentState] as const;
}

export default useSafeState;

源码中对于setState的前置做了一层判断,如果unmountedReftrue,则不更新,这也是useSafeState的核心部分。

我们再看一下useUnmountedRef的实现:

import { useEffect, useRef } from 'react';

const useUnmountedRef = () => {
  const unmountedRef = useRef(false);
  useEffect(() => {
    unmountedRef.current = false;
    return () => {
      unmountedRef.current = true;
    };
  }, []);
  return unmountedRef;
};

export default useUnmountedRef;

useUnmountedRef记录了一个ref,在销毁后为变成true,因此配合useSafeState实现了这个能力。

useResetState

基于useState二次封装,暴露出第三个参数,提供重置到最初状态的能力,用法如下:

const [state, setState, resetState] = useResetState<State>({
    hello: '',
    count: 0,
  });

源码如下:

import { useRef, useState } from 'react';
import type { Dispatch, SetStateAction } from 'react';
import { isFunction } from '../utils';
import useMemoizedFn from '../useMemoizedFn';
import useCreation from '../useCreation';

type ResetState = () => void;

const useResetState = <S>(
  initialState: S | (() => S),
): [S, Dispatch<SetStateAction<S>>, ResetState] => {
  const initialStateRef = useRef(initialState);
  const initialStateMemo = useCreation(
    () =>
      isFunction(initialStateRef.current) ? initialStateRef.current() : initialStateRef.current,
    [],
  );

  const [state, setState] = useState(initialStateMemo);

  const resetState = useMemoizedFn(() => {
    setState(initialStateMemo);
  });

  return [state, setState, resetState];
};

export default useResetState;

从源码上看,核心的部分是initialStateRef,用于保存最初始的状态值,然后暴露的reset重置方法就是将当前状态设置为initialStateRef即可。

结尾

如有理解错误的地方欢迎指出,本专栏会定期更新,最后讲解完所有Hook,欢迎关注。

如果喜欢我的文章或者想上岸大厂,可以关注公众号「量子前端」[1],将不定期关注推送前端好文、分享就业资料秘籍,也希望有机会一对一帮助你实现梦想。

转载自:https://juejin.cn/post/7402055702770073612
评论
请登录