repond
Version:
respond to items state in realtime
118 lines (101 loc) • 4.54 kB
text/typescript
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
import { toSafeEffectId } from "../helpers/effects";
import { repondMeta as meta } from "../meta";
import { AllState, EffectDef, ItemPropsByType, ItemType, PropId } from "../types";
import { startNewEffect, stopEffect } from "./effects";
import { getState } from "./getSet";
export function useStore<K_Type extends ItemType, T_ReturnedRepondProps>(
whatToReturn: (diffInfo: typeof meta.diffInfo) => T_ReturnedRepondProps,
options: Omit<EffectDef, "run">,
hookDeps: any[] = []
): T_ReturnedRepondProps {
const [, setTick] = useState(0);
const rerender = useCallback(() => setTick((tick) => tick + 1), []);
useEffect(() => {
const effectId = toSafeEffectId("reactComponent");
startNewEffect({
isPerItem: false, // isPerItem false since it's not an item effect, and it can only return one value
atStepEnd: true,
id: effectId,
run: rerender,
runAtStart: false, // runAtStart false since it's returning the initial state already, no need to set state
...options,
} as any);
return () => stopEffect(effectId);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, hookDeps);
return whatToReturn(meta.diffInfo) as T_ReturnedRepondProps;
}
export function useStoreEffect<K_Type extends ItemType>(
run: EffectDef["run"],
options: Omit<EffectDef, "run">,
hookDeps: any[] | undefined = undefined
) {
// const stringifiedCheck = JSON.stringify(check); // NOTE this may be bad for memory and performance, and might be better for people to have to manually update deps
const stringifiedCheck = JSON.stringify(options?.itemIds); // NOTE this may be bad for memory and performance, and might be better for people to have to manually update deps
useLayoutEffect(
() => {
const effectId = toSafeEffectId("useStoreEffect_" + JSON.stringify(options.changes));
startNewEffect({ id: effectId, atStepEnd: true, run, runAtStart: true, ...options } as any); // runAtStart true so it works like useEffect
return () => stopEffect(effectId);
},
hookDeps ? [...hookDeps, stringifiedCheck] : [stringifiedCheck]
// hookDeps.length > 0 ? [...hookDeps, ...(options?.itemIds ?? [])] : options?.itemIds
// hookDeps ? hookDeps : []
);
}
// NOTE types might be hard here, since it checks the first "changes" path to get the item type
export function useStoreItem<K_Type extends ItemType, T_ReturnType>(
itemEffectCallback: (itemState: AllState[K_Type][keyof AllState[K_Type]]) => T_ReturnType,
options: {
id: string;
type: K_Type;
props: ItemPropsByType[K_Type];
},
hookDeps?: any[]
) {
const { id, type, props } = options;
const didRender = useRef(false);
const returnedStateRef = useRef<AllState[K_Type][keyof AllState[K_Type]]>(getState(type, id));
// const [returnedState, setReturnedState] = useState(getState(type, id));
const [, setTick] = useState(0);
// const rerender = useCallback(() => {
// returnedStateRef.current = getState(type, id);
// setTick((tick) => tick + 1);
// }, []);
const rerender = () => {
returnedStateRef.current = getState(type, id);
setTick((tick) => tick + 1);
};
useLayoutEffect(
() => {
// if (didRender.current) setReturnedState(getState(type, id));
if (didRender.current) rerender();
const effectId = toSafeEffectId("useStoreItem" + id); // note could add JSON.stringify(check) for useful effect name
startNewEffect({
id: effectId,
// run: (itemId) => rerender(),
run() {
// returnedStateRef.current = getState(type, id);
// setTick((tick) => tick + 1);
rerender();
},
atStepEnd: true,
changes: props.map((prop) => `${type}.${prop}`) as PropId[],
itemIds: [id],
runAtStart: false, // runAtStart false since it's returning the initial state already, no need to set state
isPerItem: true,
});
didRender.current = true;
return () => stopEffect(effectId);
},
// eslint-disable-next-line react-hooks/exhaustive-deps
hookDeps && hookDeps?.length > 0 ? [...hookDeps, ...props, id] : [...props, id] // NOTE maybe need to change this to be stringified
);
// useEffect(() => {
// console.log("returnedState updated", returnedState);
// }, [returnedState]);
return itemEffectCallback(returnedStateRef.current);
// return itemEffectCallback(getState(type, id));
}
// useStoreItemPrevState