Skip to content

Instantly share code, notes, and snippets.

@binjospookie
Created September 24, 2024 10:08
Show Gist options
  • Save binjospookie/77f272e7f0fe1869d38a79b50ea8fb4c to your computer and use it in GitHub Desktop.
Save binjospookie/77f272e7f0fe1869d38a79b50ea8fb4c to your computer and use it in GitHub Desktop.
hook
/* eslint-disable @typescript-eslint/no-explicit-any, functional/immutable-data, no-restricted-syntax */
import { type Store, type StoreValue, useStoreMap } from '@grlt/vendors/state';
import { mergeDeepRight, notEquals } from '../../../helpers/helpers';
import type { NonEmptyArray, Paths } from '../../../types/types';
import { useStableKeys } from '../useStableKeys';
type GetByPath<T, Path extends string> = Path extends `${infer K}.${infer Rest}`
? K extends keyof T
? { [Key in K]: GetByPath<T[K], Rest> }
: never
: Path extends keyof T
? { [Key in Path]: T[Path] }
: never;
type MergeObjects<T> = (T extends any ? (k: T) => void : never) extends (k: infer I) => void ? I : never;
type ExtractFromState<T, Paths extends string[]> = MergeObjects<
{
[P in Paths[number]]: GetByPath<T, P>;
}[Paths[number]]
>;
const getObjectByPath = <T extends object, P extends Paths<T>>(obj: T, path: P) => {
const keys = path.split('.');
let current = obj;
const result = {};
let temp = result;
for (let i = 0; i < keys.length - 1; i++) {
// @ts-expect-error but sooo fast
temp[keys[i]] = {};
// @ts-expect-error but sooo fast
temp = temp[keys[i]];
// @ts-expect-error but sooo fast
current = current[keys[i]];
}
// @ts-expect-error but sooo fast
temp[keys[keys.length - 1]] = current[keys[keys.length - 1]];
return result;
};
const createHookWithStoreGetter = <S extends Store<any>>($kv: S) => {
type State = StoreValue<S>;
type PathList = NonEmptyArray<Paths<State>>;
type Config<T extends PathList> = {
fn: (_: ExtractFromState<State, T>) => any;
keys: [any] | readonly any[] | any[];
};
type ConfigShort<T extends PathList> = (_: ExtractFromState<State, T>) => any;
function useKv<P extends PathList>(paths: P): ExtractFromState<State, P>;
function useKv<P extends PathList, C extends Config<P>>(paths: P, config: C): ReturnType<(typeof config)['fn']>;
function useKv<P extends PathList, C extends ConfigShort<P>>(paths: P, config: C): ReturnType<typeof config>;
// eslint-disable-next-line no-restricted-syntax
function useKv<P extends PathList, C extends Config<P> | ConfigShort<P> | undefined>(paths: P, config?: C) {
// @ts-expect-error leave me alone
const memoizedPaths = useStableKeys([...paths, ...(config?.keys ?? [])]);
return useStoreMap({
store: $kv,
fn: (x) => {
const res = paths.reduce(
(result, path) => mergeDeepRight(result, getObjectByPath(x, path)),
{},
) as ExtractFromState<State, P>;
return config ? (typeof config === 'function' ? config(res) : config.fn(res)) : res;
},
keys: [memoizedPaths],
updateFilter: notEquals,
});
}
return useKv;
};
export { createHookWithStoreGetter };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment