Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | 1x 43x 43x 43x 43x 43x 43x 43x 43x 43x 43x 43x 2x 1x 43x 12x 12x 23x 12x 12x 43x 17x 5x 17x 17x 43x 29x 8x 8x 43x 22x 5x 5x 5x 43x 17x 1x 17x 43x 8x 8x 1x 1x | import React, { useState, useEffect, useRef } from "react";
export type UseStableStateProps<T> = {
initialState: T;
delay?: number;
load?: () => Promise<T>;
onStableStateChanged?: () => Promise<void>;
onBeforeUnload?: (
params: UseStableStateExtraParams<T>,
event: BeforeUnloadEvent
) => boolean | undefined;
};
export type UseStableStateParams<T> = [
T,
T,
React.Dispatch<React.SetStateAction<T>>
];
export type UseStableStateExtraParams<T> = {
state: T;
stableState: T;
setState: React.Dispatch<React.SetStateAction<T>>;
isEditing: boolean;
delay: number;
setDelay: React.Dispatch<React.SetStateAction<number>>;
};
function useStableStateExtra<T>(
options: UseStableStateProps<T>
): UseStableStateExtraParams<T> {
const [state, setState] = useState(options.initialState);
const [stableState, setStableState] = useState(options.initialState);
const lastEditTimeRef = useRef(new Date());
const [editCount, setEditCount] = useState(0);
const [saveTrigger, setSaveTrigger] = useState(false);
const [isEditing, setIsEditing] = useState(false);
const [delay, setDelay] = useState(options.delay ?? 1000);
const isLoaded = useRef(false);
const isInitialChange = useRef(true);
window.onbeforeunload = (event) => {
if (!options.onBeforeUnload) return;
return options.onBeforeUnload(
{
state,
stableState,
setState,
isEditing,
delay,
setDelay,
},
event
);
};
useEffect(() => {
if (!isLoaded.current) {
(async () => {
if (options.load) setState(await options.load());
isInitialChange.current = true;
isLoaded.current = true;
})();
}
}, [setState]);
useEffect(() => {
if (editCount > 0) {
setIsEditing(true);
}
lastEditTimeRef.current = new Date();
setEditCount(editCount + 1);
}, [state, setEditCount]);
useEffect(() => {
setTimeout(() => {
if (new Date().getTime() - lastEditTimeRef.current.getTime() >= delay) {
setSaveTrigger(true);
}
}, delay);
}, [editCount, setSaveTrigger]);
useEffect(() => {
if (saveTrigger) {
setStableState(state);
setSaveTrigger(false);
setIsEditing(false);
}
}, [saveTrigger]);
useEffect(() => {
if (!isInitialChange.current && options.onStableStateChanged) {
options.onStableStateChanged();
}
isInitialChange.current = false;
}, [stableState]);
return { state, stableState, setState, isEditing, delay, setDelay } as const;
}
function useStableState<T>(
options: UseStableStateProps<T>
): UseStableStateParams<T> {
const { state, stableState, setState } = useStableStateExtra(options);
return [state, stableState, setState];
}
export { useStableState, useStableStateExtra };
export default { useStableState, useStableStateExtra };
|