// Turn any hook into a ✨ nook ✨const mountInterval = nook(useInterval);
function GranularMount() { // Animation state const [walkIdx, incrementWalkIdx] = useModuloCounter(WALKING_FRAMES.length); const [lookIdx, incrementLookIdx] = useModuloCounter(LOOKING_FRAMES.length); const [walk, toggleWalk] = useToggle(true); const [look, toggleLook] = useToggle(true);
// You can use nooks inside regular components via // the `useNook` hook useNook(() => { walk && mountInterval``(incrementWalkIdx, 100); look && mountInterval``(incrementLookIdx, 100); });
return ( <div className="grid gap-2 max-w-sm place-items-center place-content-center mx-auto mt-12 grid-cols-2"> <pre className={walk ? '' : 'opacity-50'}> <code>{WALKING_FRAMES[walkIdx]}</code> </pre> <pre className={look ? '' : 'opacity-50'}> <code>{LOOKING_FRAMES[lookIdx]}</code> </pre> <Btn onClick={toggleWalk}>{walk ? 'Pause' : 'Play'}</Btn> <Btn onClick={toggleLook}>{look ? 'Pause' : 'Play'}</Btn> </div> );}
import { useCallback, useEffect, useState } from 'react';
export function useModuloCounter(mod: number) { const [value, setValue] = useState(0);
const increment = useCallback( () => setValue((prev) => (prev + 1) % mod), [mod], );
return [value, increment] as const;}
export function useToggle(initial: boolean) { const [value, setValue] = useState(initial); const toggle = useCallback(() => setValue((prev) => !prev), []);
return [value, toggle] as const;}
export function useInterval(callback: () => unknown, interval: number) { // We can use vanilla React hooks inside "nooks", and resort to builtin nooks // when we need to call something conditionally useEffect(() => { const handle = window.setInterval(callback, interval); return () => window.clearInterval(handle); }, [interval, callback]);}