A manually maintained reference for the public React utilities in this repository.
Keep it aligned with index.ts, implementation behavior, and llms.txt.
pnpm add @mj-studio/react-utilimport { useTicker } from '@mj-studio/react-util'Runs a callback once after the component mounts. The callback return value is ignored.
useMount(() => {
analytics.track('screen_open')
})Runs a callback on a fixed interval and always uses the latest callback reference.
useIntervalCallback(() => {
refreshClock()
}, 1, true)Returns timeout helpers that automatically clean up tracked timers on unmount.
const { setAutoClearTimeout, clearAllTimers } = useTimeoutHandlers()
useMount(() => {
setAutoClearTimeout(() => {
setOpen(false)
}, 3000)
})Clears every tracked timeout and resets the internal timer list.
Registers an existing timeout id so it is cleared on unmount. Pass { withClear: true } to clear already tracked timers first.
Creates a timeout, tracks it automatically, and returns the timeout id.
Returns a stable callback reference that always delegates to the latest callback implementation.
const onTick = useStableCallback(() => {
console.log(latestValue)
})Runs a callback once when the component unmounts. The callback should perform its own cleanup work.
useUnmount(() => {
socket.close()
})Runs a callback once during the first render before the component paints. The callback return value is ignored.
useMountBeforeRender(() => {
cacheRef.current = createCache()
})Returns true after the component has mounted on the client.
const isClient = useIsClient()
return isClient ? <ClientOnlyChart /> : nullRuns an effect only after the initial render has been skipped. The callback return value is ignored.
useEffectWithoutFirst(() => {
saveDraft(formState)
}, [formState])Returns predicates for checking whether the component has mounted or already unmounted.
const { checkUnmounted } = useLifecycle()
fetchData().then(() => {
if (!checkUnmounted()) {
setReady(true)
}
})Lazily creates a stable value once and keeps it for the component lifetime.
const instanceId = useRefValue(() => crypto.randomUUID())Subscribes to the browser beforeunload event. This hook is browser-only and throws when the required DOM APIs are unavailable.
useBeforeunloadDom((event) => {
if (!hasUnsavedChanges) {
return
}
event.preventDefault()
return 'You have unsaved changes.'
})Renders the current interval tick through a render prop.
<IntervalHandler intervalSec={1} doImmediately={true}>
{({ tick }) => <span>{tick}</span>}
</IntervalHandler>BeforeunloadDom(props: { onBeforeunload: (e: BeforeUnloadEvent) => string | undefined | void; children?: ReactNode })
Registers a beforeunload listener and renders children unchanged.
<BeforeunloadDom onBeforeunload={() => 'You have unsaved changes.'}>
<Editor />
</BeforeunloadDom>Imperative ticker that emits elapsed time on a fixed interval.
const ticker = new Ticker()
ticker.start({
handler: (elapsedSec) => {
console.log(elapsedSec)
},
})Current lifecycle state of the ticker. The value is one of 'initial', 'pause', or 'progress'.
Starts the ticker from zero with a handler and optional interval settings.
Resumes ticking from the current accumulated time.
Pauses ticking and preserves the accumulated elapsed time.
Stops the ticker and resets its accumulated time to zero.
Creates ticker state and imperative controls for elapsed time updates.
const { tickSec, startTicker, pauseTicker } = useTicker({
onComplete: () => {
console.log('done')
},
})
useMount(() => {
startTicker({ durationSec: 10 })
})Ticker lifecycle state. The value is one of 'initial', 'run_pause', 'run_progress', or 'complete'.
Current elapsed time in ticker units.
Starts the ticker. durationSec defaults to a large sentinel value so the ticker can run without an explicit end.
Pauses the active ticker when it is running.
Resumes a paused ticker. If startAtResumeIfNeeded is enabled, this can start a fresh ticker from the initial state.
Resets the ticker state and clears the elapsed time.
Render-prop component that subscribes to ticker updates and renders { tickSec }.
<TickerComponent>
{({ tickSec }) => <span>{tickSec}</span>}
</TickerComponent>Creates ticker controls that count down from a duration instead of counting up from zero.
const { tickSec, startTicker } = useReverseTicker({})
useMount(() => {
startTicker({ durationSec: 30 })
})Remaining time derived from the original duration minus the elapsed ticker time.
Starts the reverse ticker. Negative durations are ignored.
Resets the reverse ticker and clears the stored duration.
Creates countdown text and controls for a target due date.
const { dueDateText, startTickerWithISO8601 } = useDueDateTicker({
secondsFormat: 'mm:ss',
})
useMount(() => {
startTickerWithISO8601('2030-01-01T00:00:00.000Z')
})Formatted remaining time text generated with @mj-studio/js-util second-format helpers.
Remaining seconds from the target due date.
Whether the target date has already passed or the countdown completed.
Starts the countdown from a unix timestamp. Thirteen-digit millisecond values are normalized to seconds automatically.
Starts the countdown from an ISO-8601 date string. Invalid strings are ignored.
DueDateText(props: { dueDate: string | number; children: (text: string, meta: { remainSeconds: number; isExpired: boolean }) => ReactElement } & DueDateTickerProps)
Renders formatted due-date text through a render prop.
<DueDateText dueDate={"2030-01-01T00:00:00.000Z"}>
{(text, { isExpired }) => <span>{isExpired ? 'Expired' : text}</span>}
</DueDateText>Creates a React context helper tuple with a required hook, provider, consumer, optional hook, and raw context.
Tuple order:
[useRequiredContext, Provider, Consumer, useOptionalContext, Context]
const [useAuth, AuthProvider] = createCtx<{ userId: string }, { userId: string }>(
({ userId }) => ({ userId }),
'Auth',
)Serializes search params into a query-string fragment without a leading ?.
getSearchParams({ page: '1', q: 'react' }) // Returns: 'page=1&q=react'Copies text to the system clipboard in a browser runtime.
await copyTextToClipboardDom('Hello world')Copies an image data URI to the system clipboard in a browser runtime.
await copyImageToClipboardDom('data:image/png;base64,...')Moves focus away from the currently focused element in a browser runtime.
blurFocusDom()Global in-memory event emitter instance for app-level events.
AppEvent.emitEvent('toast', { message: 'Saved' })Emits an event to every registered listener and returns whether any listener was called.
Emits an event and waits for asynchronous listeners in registration order.
Registers a listener for the given event type.
Removes a previously registered listener from the given event type.
Subscribes a component to synchronous events from AppEvent.
useAppEventListener('toast', ({ message }) => {
console.log(message)
})useAsyncAppEventListener<T>(type: string, listener: AppEventAsyncListener<T>, unsubscribe?: () => void): void
Subscribes a component to asynchronous events intended for AppEvent.awaitEmitEvent.
useAsyncAppEventListener('save', async (payload) => {
await persist(payload)
})Configuration for useTicker and useReverseTicker.
onComplete?: () => voidstartAtResumeIfNeeded?: booleandisableTickSecUpdate?: boolean
Configuration for useDueDateTicker and DueDateText.
secondsFormat?: SecFormats
Ticker lifecycle state used by Ticker.
Callback signature used by Ticker.start.
Render-prop component props used by IntervalHandler.
Callback used by createCtx to transform provider children before render.
Readonly tuple returned by createCtx.
Synchronous listener signature used by AppEvent.
Asynchronous listener signature used by AppEvent.awaitEmitEvent.