🪝
react-hooks The set of general-purpose React hooks.
npm install --save-prod @smikhalevski/react-hooks
State
usePrevState
useRefCallback
useRenderedValueRef
useSemanticCallback
useSemanticMemo
useExecution
useExecutor
useToggle
Rendering
Time
User flow
State
usePrevState
Compares the state passed during the previous render with the newly given state, and if they differ based on equality checker, then the new state is returned.
const nextValue = usePrevState(
value,
(prevValue, value) => prevValue !== value,
);
useRefCallback
Returns a ref object and a callback to update the value of this ref.
const [ref, updateRef] = useRefCallback(initialValue);
useRenderedValueRef
Creates a MutableRefObject
that keeps ref to the given value. This hook comes in handy if you want to use the props
provided during the most recent render in the async context.
const valueRef = useRenderedValueRef(value);
useSemanticCallback
The drop-in replacement for React.useCallback
which provides the semantic guarantee that the callback won't be "
forgotten" until the hook is unmounted.
const memoizedCallback = useSemanticCallback(
() => doSomething(a, b),
[a, b],
);
useSemanticMemo
The drop-in replacement for React.useMemo
which provides thee semantic guarantee that the value produced by factory
won't be "forgotten" until the hook is unmounted.
const memoizedValue = useSemanticMemo(
() => computeExpensiveValue(a, b),
[a, b],
);
useExecution
Executes a callback when dependencies are changed and returns an
Executor
instance that describes the result
and status.
const executor = useExecution(
async (signal) => doSomething(a, b),
[a, b],
);
useExecutor
Creates a new Executor
instance that provides
means to call, abort and monitor async callbacks.
const executor = useExecutor(initialValue);
// Starts a new execution.
// If there's pending execution, it is aborted via signal.
executor.execute(async (signal) => doSomething());
You can manage how executors are created with ExecutorProvider
and
SsrExecutorProvider
.
import {renderToString} from 'react-dom';
import {SsrExecutorProvider, ExecutorProviderContext} from '@smikhalevski/react-hooks';
const mySsrExecutorProvider = new SsrExecutorProvider();
renderToString(
<ExecutorProviderContext.Provider value={mySsrExecutorProvider}>
{/* */}
</ExecutorProviderContext.Provider>
);
// Waits for all executors to complete pending executions
await mySsrExecutorProvider.waitForExecutorsToComplete();
You can create a custom useExecutor
hook that is bound to a custom context.
import {createContext} from 'react';
import {createExecutorHook, ExecutorProvider} from '@smikhalevski/react-hooks';
const MyExecutorProviderContext = createContext(new ExecutorProvider());
const useMyExecutor = createExecutorHook(MyExecutorProviderContext);
useToggle
Returns a boolean flag and functions to toggle its value.
const [enabled, enable, disable] = useToggle(initialValue);
Rendering
useRerender
Returns a callback that triggers a component re-render. Re-render callback can be safely invoked at any time of the component life cycle. Returned callback doesn't change between hook invocations.
Note: Using this hook makes your code imperative, which is generally considered a bad practice.
const rerender = useRerender();
rerender();
useMountSignal
Returns AbortSignal
that is aborted when the component is unmounted.
const signal = useMountSignal();
// Returns true if componenet was unmounted
signal.aborted;
useRenderEffect
Analogue of React.useEffect
that invokes an effect
synchronously during rendering if deps
aren't defined or don't
equal to deps provided during the previous render. This hook comes in handy when calling an effect during SSR.
The optional cleanup callback is called synchronously during rendering.
useRenderEffect(
() => {
doSomething(a, b);
return () => {
cleanup();
};
},
[a, b],
);
useEffectOnce
Same as React.useEffect
but calls effect only once after the component is mounted.
The optional cleanup callback is called when the component is unmounted.
useEffectOnce(() => {
doSomething(a, b);
return () => {
cleanup();
};
});
useRenderEffectOnce
Same as useRenderEffect
but calls effect only once after the component is mounted.
The optional cleanup callback is called when the component is unmounted.
useRenderEffectOnce(() => {
doSomething(a, b);
return () => {
cleanup();
};
});
useRerenderSchedule
Re-renders the component on periodic interval.
useRerenderSchedule(500);
Time
useTime
Returns the Time
instance that provides the current
timestamp.
const time = useTime();
// Use this instead of Date.now()
time.now();
You can alter the timestamp by providing the custom
Time
implementation.
import {renderToString} from 'react-dom';
import {Time, TimeContext} from '@smikhalevski/react-hooks';
const myTime = new Time();
// After this, myTime.now() would return the timestamp
// that is 1 munute ahead of the Date.now()
myTime.setTimestamp(Date.now() + 60_000);
renderToString(
<TimeContext.Provider value={myTime}>
{/* */}
</TimeContext.Provider>
);
useAnimationFrame
Returns protocol to start and stop an animation loop.
When start
is called the animation loop starts invoking the provided callback using requestAnimationFrame
. If the
animation was already pending then it is stopped and started with the new callback.
const [start, stop] = useAnimationFrame();
// Cancels pending animation loop and schedules the new animation loop
start(() => {
// Apply animation changes
});
// Stop the animation
stop();
useMetronome
Returns a Metronome
instance. Use this to
schedule callback invocation.
const metronome = useMetronome(500);
useEffect(
() => metronome.schedule(() => {
// Invoked every 500 ms
doSomething();
}),
[metronome],
);
You can alter how metronomes are created by providing the custom
MetronomeProvider
implementation.
import {renderToString} from 'react-dom';
import {MetronomeProvider, MetronomeProviderContext} from '@smikhalevski/react-hooks';
const myMetronomeProvider = new MetronomeProvider();
renderToString(
<MetronomeProviderContext.Provider value={myMetronomeProvider}>
{/* */}
</MetronomeProviderContext.Provider>
);
useSchedule
The replacement for setInterval
that is cancelled when component is unmounted. Schedules a function to be repeatedly
called with a fixed time delay between each call.
All functions that were scheduled with the same delay are invoked synchronously.
const [schedule, cancel] = useSchedule();
// Cancels currently scheduled callback and schedules the new one
schedule(
(a, b) => {
doSomething(a, b);
},
500, // Interval delay
a, b, // Varargs that are passed to the callback
);
// Stops invoking the callback that was last provided to schedule()
cancel();
useDebounce
The replacement for setTimeout
that is cancelled when component is unmounted.
const [debounce, cancel] = useDebounce();
// Cancels pending debounce and schedules the new call
debounce(
(a, b) => {
doSomething(a, b);
},
500, // Timeout after which the callback is called
a, b, // Varargs that are passed to the callback
);
// Cancels the last debounce call
cancel();
useDebouncedState
Returns stateful values and a function to update them. Upon invocation of setState
, the nextState
is assigned
synchronously, and the component is re-rendered. After the delay
the currState
is set to nextState
and component
is re-rendered again.
const [currState, nextState, setState] = useDebouncedState(500);
User flow
useBlocker
Provides mechanism for blocking async processes and unblocking them from an external context.
const blocker = useBlocker<boolean>();
// Returns Promise that is resolved with the value passed to blocker.unblock(value)
blocker.block(); // → Promise<boolean>
// Unblocks the blocker with given value.
blocker.unblock(true);
useGuard
Extract shared conditional logic from event handlers and callbacks.
const guard = useGuard(
async () => checkCondition(),
async (replay) => {
// Invoked if the guarded callback was called when condition wasn't met.
doFallback();
// Invoke the guarded callback with the same arguments.
replay();
},
);
const myGuardedCallback = guard.guardCallback((a, b) => {
myCallback(a, b);
});
myGuardedCallback(a, b);