import { useEffect, useRef, useState } from "react"; export const useLocal = ( data: T, effect?: (arg: { init: boolean; setDelayedRender: (arg: boolean) => void; }) => Promise void)> | void | (() => void), deps?: any[] ): { [K in keyof T]: T[K] extends Promise ? null | Awaited : T[K]; } & { render: () => void } => { const [, _render] = useState({}); const _ = useRef({ data: data as unknown as T & { render: () => void; }, deps: (deps || []) as any[], ready: false, _loading: {} as any, lastRender: 0, lastRenderCount: 0, delayedRender: false, delayedRenderTimeout: null as any, overRenderTimeout: null as any, }); const local = _.current; useEffect(() => { local.ready = true; if (effect) effect({ init: true, setDelayedRender(arg) { local.delayedRender = arg; }, }); }, []); if (local.ready === false) { local._loading = {}; local.data.render = () => { if (local.ready) { if (local.delayedRender) { if (Date.now() - local.lastRender > 100) { local.lastRender = Date.now(); _render({}); } else { clearTimeout(local.delayedRenderTimeout); local.delayedRenderTimeout = setTimeout(local.data.render, 50); } return; } if (Date.now() - local.lastRender < 300) { local.lastRenderCount++; } else { local.lastRenderCount = 0; } local.lastRender = Date.now(); if (local.lastRenderCount > 300) { clearTimeout(local.overRenderTimeout); local.overRenderTimeout = setTimeout(() => { local.lastRenderCount = 0; local.lastRender = Date.now(); _render({}); }, 1000); console.error( `local.render executed ${local.lastRenderCount} times in less than 300ms` ); return; } _render({}); } }; } else { if (local.deps.length > 0 && deps) { for (const [k, dep] of Object.entries(deps) as any) { if (local.deps[k] !== dep) { local.deps[k] = dep; if (effect) { setTimeout(() => { effect({ init: false, setDelayedRender(arg) { local.delayedRender = arg; }, }); }); } break; } } } } return local.data as any; };