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.