104 lines
2.5 KiB
TypeScript
104 lines
2.5 KiB
TypeScript
import { useEffect, useRef, useState } from "react";
|
|
|
|
export const useLocal = <T extends object>(
|
|
data: T,
|
|
effect?: (arg: {
|
|
init: boolean;
|
|
setDelayedRender: (arg: boolean) => void;
|
|
}) => Promise<void | (() => void)> | void | (() => void),
|
|
deps?: any[]
|
|
): {
|
|
[K in keyof T]: T[K] extends Promise<any> ? null | Awaited<T[K]> : 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;
|
|
}; |