@state-less/leap-frontend
Version:
A collection of open source fullstack services powered by React Server
76 lines (65 loc) • 1.85 kB
text/typescript
import { useEffect, useRef, useState } from 'react';
export const useSyncedState = (
defValue,
updateFn,
{
draft,
successFn,
}: { draft?: boolean; successFn?: (value?: unknown) => void } = {
draft: false,
}
) => {
const timeout = useRef<number | NodeJS.Timeout | null>(null);
const timestampSave = useRef(0);
const timestampLocal = useRef(0);
const [localValue, setLocalValue] = useState(defValue);
const [loading, setLoading] = useState(false);
const setValue = async (value) => {
setLocalValue(value);
timestampLocal.current = +new Date();
if (timeout.current) clearTimeout(timeout.current);
const save = async () => {
setLoading(true);
timestampSave.current = +new Date();
await updateFn(value);
successFn?.(value);
};
if (draft) {
await save();
} else {
timeout.current = setTimeout(async () => {
await save();
}, 1500);
}
};
useEffect(() => {
if (timestampLocal <= timestampSave) {
setLocalValue(defValue);
}
setLoading(false);
}, [defValue]);
return [localValue, setValue, { loading }];
};
export const useIsOffScreen = () => {
const [isOffScreen, setIsOffScreen] = useState(false);
const ref = useRef(null);
useEffect(() => {
if (!ref?.current) return;
const obs = new IntersectionObserver(
(entries) => {
if (entries[0]?.isIntersecting) {
setIsOffScreen(true);
} else if (entries[0]?.boundingClientRect?.y > 0) {
setIsOffScreen(false);
}
},
{
root: document.body,
rootMargin: '0px 0px -100%',
threshold: 0.0,
}
);
obs.observe(ref?.current);
}, [setIsOffScreen]);
return [ref, isOffScreen];
};