This series is to interpret all the ahooks source code, through the interpretation of ahooks source code to get familiar with the writing of custom hook, improve their own ability to write custom hook, hope to be helpful to you.

Review past

  • Ahooks source code interpretation series

useEventEmitter

Hook version of event Bus, used for multi-component communication.

import { useRef, useEffect } from 'react';

type Subscription<T> = (val: T) = > void;
/// Ultra simple version of the event manager
/// Use the set as a container. The event type is not recorded when the event is registered, so only all events are triggered when the event is triggered
export class EventEmitter<T> {
  private subscriptions = new Set<Subscription<T>>();

  emit = (val: T) = > {
    for (const subscription of this.subscriptions) { subscription(val); }}; useSubscription =(callback: Subscription<T>) = > {
    /// there is a lot of this code in ahooks, which means always use the latest CB when running
    /// The logic will not be repeated because of the cb update
    const callbackRef = useRef<Subscription<T>>();
    callbackRef.current = callback;
    useEffect(() = > {
      // The new subscription function is used to execute the CB, so even the same CB will be registered twice
      function subscription(val: T) {
        if(callbackRef.current) { callbackRef.current(val); }}this.subscriptions.add(subscription);
      return () = > {
        this.subscriptions.delete(subscription); }; } []); }; }export default function useEventEmitter<T = void> () {
  const ref = useRef<EventEmitter<T>>();
  if(! ref.current) { ref.current =new EventEmitter();
  }
  return ref.current;
}

Copy the code

useLockFn

Asynchronous locks lock the incoming FN, but cannot lock the generated CB

import { useRef, useCallback } from 'react';

function useLockFn<P extends any[] = any[].V extends any = any> (fn: (... args: P) =>Promise<V>) {
  /// The id of whether to be in the asynchronous request. If it is already in the request, it will refuse to execute fn directly
  const lockRef = useRef(false);

  return useCallback(
    async(... args: P) => {/// Note that the direct return here is equivalent to the current anonymous function returning promise.resolve (undefined)
      /// For example: const submit = useLockFn(fn);
      / / / [1, 2, 3]. The forEach (n = > submit (). Then (fn2))
      Fn is executed only once, but fn2 is executed three times, in order of 2, 3, and 1
      /// So all subsequent logic should be processed in FN, not in CB
      if (lockRef.current) return;
      lockRef.current = true;
      try {
        const ret = awaitfn(... args); lockRef.current =false;
        return ret;
      } catch (e) {
        lockRef.current = false;
        throw e;
      }
    },
    [fn],
  );
}

export default useLockFn;

Copy the code

usePersisFn

To get a “steady as a dog” function whose reference never changes, how do you read a constantly changing value from useCallback? An abstract implementation of.

import { useRef } from 'react';

export type noop = (. args: any[]) = > any;

function usePersistFn<T extends noop> (fn: T) {
  const fnRef = useRef<T>(fn);
  fnRef.current = fn;
	/// Guarantee that the returned function reference will never change
  const persistFn = useRef<T>();
  if(! persistFn.current) {/// The anonymous function is always returned, so the reference does not change
    persistFn.current = function (. args) {
      returnfnRef.current! .apply(this, args);
    } as T;
  }

  returnpersistFn.current! ; }export default usePersistFn;

Copy the code

The above content due to my level of problems inevitably wrong, welcome to discuss feedback.