@supunlakmal/hooks
Version:
A collection of reusable React hooks
65 lines • 2.11 kB
JavaScript
import { useCallback, useRef, useState, useEffect, } from 'react';
/**
* Returns a boolean that is `true` only on first render.
*/
const useFirstMountState = () => {
const isFirstMount = useRef(true);
useEffect(() => {
isFirstMount.current = false;
}, []);
return isFirstMount.current;
};
const stateChanger = (state) => (state + 1) % Number.MAX_SAFE_INTEGER;
/**
* Return callback function that re-renders component.
*/
const useRerender = () => {
// eslint-disable-next-line react/hook-use-state
const [, setState] = useState(0);
return useCallback(() => setState(stateChanger), []);
};
const initState = (initialState) => {
if (typeof initialState === 'function') {
initialState = initialState();
}
return initialState;
};
const updateState = (nextState, previousState) => {
if (typeof nextState === 'function') {
return nextState(previousState);
}
return nextState;
};
const resolveHookState = (...args) => {
if (args.length === 1) {
return initState(args[0]);
}
return updateState(args[0], args[1]);
};
/**
* Like `React.useState`, but its state setter accepts extra argument, that allows to cancel
* rerender.
*/
export const useControlledRerenderState = (initialState) => {
const isFirstMount = useFirstMountState();
const state = useRef(useFirstMountState()
? (initialState instanceof Function ? initialState() : initialState)
: undefined);
const rr = useRerender();
return [
state.current,
useCallback((value, rerender) => {
const newState = resolveHookState(value, state.current);
if (isFirstMount) {
state.current = (initialState instanceof Function ? initialState() : initialState);
}
if (newState !== state.current) {
state.current = newState;
if (rerender === undefined || rerender) {
rr();
}
}
}, [rr, initialState, isFirstMount]),
];
};
//# sourceMappingURL=useControlledRerenderState.js.map