You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
65 lines
1.5 KiB
65 lines
1.5 KiB
// @flow
|
|
import { useRef, useState, useEffect } from 'react';
|
|
import areInputsEqual from './are-inputs-equal';
|
|
|
|
type Cache<T> = {|
|
|
inputs: ?(mixed[]),
|
|
result: T,
|
|
|};
|
|
|
|
export function useMemoOne<T>(
|
|
// getResult changes on every call,
|
|
getResult: () => T,
|
|
// the inputs array changes on every call
|
|
inputs?: mixed[],
|
|
): T {
|
|
// using useState to generate initial value as it is lazy
|
|
const initial: Cache<T> = useState(() => ({
|
|
inputs,
|
|
result: getResult(),
|
|
}))[0];
|
|
const isFirstRun = useRef<boolean>(true);
|
|
const committed = useRef<Cache<T>>(initial);
|
|
|
|
// persist any uncommitted changes after they have been committed
|
|
const useCache: boolean =
|
|
isFirstRun.current ||
|
|
Boolean(
|
|
inputs &&
|
|
committed.current.inputs &&
|
|
areInputsEqual(inputs, committed.current.inputs),
|
|
);
|
|
|
|
// create a new cache if required
|
|
const cache: Cache<T> = useCache
|
|
? committed.current
|
|
: {
|
|
inputs,
|
|
result: getResult(),
|
|
};
|
|
|
|
// commit the cache
|
|
useEffect(() => {
|
|
isFirstRun.current = false;
|
|
committed.current = cache;
|
|
}, [cache]);
|
|
|
|
return cache.result;
|
|
}
|
|
|
|
export function useCallbackOne<T: Function>(
|
|
// getResult changes on every call,
|
|
callback: T,
|
|
// the inputs array changes on every call
|
|
inputs?: mixed[],
|
|
): T {
|
|
return useMemoOne(() => callback, inputs);
|
|
}
|
|
|
|
// Aliased exports
|
|
// A drop in replacement for useMemo and useCallback that plays
|
|
// very well with eslint-plugin-react-hooks
|
|
|
|
export const useMemo = useMemoOne;
|
|
export const useCallback = useCallbackOne;
|