prasi-lib/utils/use-local.ts

71 lines
1.7 KiB
TypeScript
Executable File

import { useEffect, useRef, useState } from "react";
export const useLocal = <T extends object>(
data: T,
effect?: (arg: {
init: boolean;
}) => Promise<void | (() => void)> | void | (() => void),
deps?: any[]
): {
[K in keyof T]: T[K] extends Promise<any> ? null | Awaited<T[K]> : T[K];
} & { render: (force?: boolean) => void } => {
const [, _render] = useState({});
const _ = useRef({
data: data as unknown as T & {
render: (force?: boolean) => void;
},
deps: (deps || []) as any[],
promisedKeys: new Set<string>(),
ready: false,
_loading: {} as any,
});
const local = _.current;
useEffect(() => {
local.ready = true;
if (effect) effect({ init: true });
}, []);
if (local.ready === false) {
local._loading = {};
for (const [k, v] of Object.entries(data)) {
if (!local.promisedKeys.has(k)) {
let val = v;
if (typeof val === "object" && val instanceof Promise) {
local._loading[k] = true;
local.promisedKeys.add(k);
(local.data as any)[k] = null;
val.then((resolved) => {
(local.data as any)[k] = resolved;
local._loading[k] = false;
local.data.render();
});
}
}
}
local.data.render = (force) => {
if (force) _render({})
else if (local.ready) _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 });
});
}
break;
}
}
}
}
return local.data as any;
};