Skip to content

useNow and useTickingText ​

A clock for a surface that says how long something has been running.

tsx
import { useNow, useTickingText } from '@basmilius/react-ui';
tsx
import { useState } from 'react';
import { Button, useNow, useTickingText } from '@basmilius/react-ui';
import { formatClockDuration, formatElapsedShort } from '@basmilius/react-ui/format';

export default function UseNowDemo() {
    const [startedAt, setStartedAt] = useState<number | null>(null);
    const now = useNow(1000, startedAt !== null);
    const [mountedAt] = useState(Date.now);
    const ticking = useTickingText(() => formatClockDuration(Date.now() - mountedAt));

    return (
        <div className="flex flex-col items-center gap-3 text-sm text-text">
            <p className="tabular-nums">{startedAt === null ? 'Not running' : `Running for ${formatElapsedShort(now - startedAt)}`}</p>
            <Button variant="secondary" onClick={() => setStartedAt(startedAt === null ? Date.now() : null)}>
                {startedAt === null ? 'Start' : 'Stop'}
            </Button>
            <p className="text-xs text-text-muted">
                On this page for <span ref={ticking} className="tabular-nums" />
            </p>
        </div>
    );
}

useNow ​

useNow(intervalMs, ticking = true) answers the time in epoch milliseconds and draws the component again every intervalMs. One timer serves the whole surface, and it runs only while ticking is true, so a list with nothing running costs nothing. It starts at the moment the component mounts, so a panel that comes back halfway through does not read zero for a second.

useTickingText ​

useTickingText(render, intervalMs = 1000) writes the text straight into a node and answers the ref to put on it. Nothing re-renders. Use it for a timer on one line of a long list or a thread, where re-rendering every row each second would cost more than the timer is worth.

tsx
const ref = useTickingText(() => formatElapsedShort(Date.now() - startedAt));

return <span ref={ref} />;

It writes after every render as well, so the first render already shows the text, and a language or region change lands without waiting for the next tick.