Last active
September 7, 2021 19:20
-
-
Save KEIII/165c9687e5a6b7de058a9e21bb41d5ce to your computer and use it in GitHub Desktop.
React hook useFactory()
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useLayoutEffect, useRef, useState } from 'react'; | |
export type Dispose = () => void | undefined; | |
export type Disposable<T> = { | |
instance: T; | |
dispose: Dispose; | |
}; | |
export type Factory<Arguments extends Tuple, Instance> = ( | |
...props: Arguments | |
) => Disposable<Instance>; | |
export type Tuple = Array<unknown> & | |
( | |
| [void] | |
| { | |
length: number; | |
every: (cb: (v: unknown, k: number) => boolean) => boolean; | |
[k: number]: unknown; | |
} | |
); | |
const shallowCompare = <T extends Tuple>(a: T, b: T) => { | |
return a.length === b.length && a.every((v, k) => v === b[k]); | |
}; | |
/** | |
* Returns an instance from calling f(...args). | |
* Actually it's like useMemo() but support cleanup function. | |
*/ | |
export const useFactory = <Arguments extends Tuple, Instance>( | |
f: Factory<Arguments, Instance>, | |
args: Arguments, | |
): Instance => { | |
const intoState = () => [args, f(...args)] as const; | |
const [[prevArgs, { instance, dispose }], setState] = useState(intoState); | |
// Creating a new instance on `args` changes | |
// eslint-disable-next-line react-hooks/exhaustive-deps | |
useLayoutEffect( | |
() => { | |
if (shallowCompare(args, prevArgs)) return; | |
dispose?.(); | |
setState(intoState); | |
}, | |
// Call each render | |
); | |
// Dispose on component destroy | |
// Use ref to call actual dispose function | |
{ | |
const disposeRef = useRef<Dispose>(); | |
disposeRef.current = dispose; | |
useLayoutEffect(() => { | |
return () => disposeRef.current?.(); | |
}, []); | |
} | |
return instance; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment