75 lines
1.9 KiB
TypeScript
75 lines
1.9 KiB
TypeScript
![]() |
import type { Ref, UnwrapRef } from 'vue';
|
||
|
import { onBeforeUnmount, ref } from 'vue';
|
||
|
|
||
|
export type Updater<State> = (prev: State) => State;
|
||
|
|
||
|
/**
|
||
|
* Execute code before next frame but async
|
||
|
*/
|
||
|
export function useLayoutState<State>(
|
||
|
defaultState: State,
|
||
|
): [Ref<State>, (updater: Updater<State>) => void] {
|
||
|
const stateRef = ref<State>(defaultState);
|
||
|
// const [, forceUpdate] = useState({});
|
||
|
|
||
|
const lastPromiseRef = ref<Promise<void>>(null);
|
||
|
const updateBatchRef = ref<Updater<State>[]>([]);
|
||
|
function setFrameState(updater: Updater<State>) {
|
||
|
updateBatchRef.value.push(updater);
|
||
|
|
||
|
const promise = Promise.resolve();
|
||
|
lastPromiseRef.value = promise;
|
||
|
|
||
|
promise.then(() => {
|
||
|
if (lastPromiseRef.value === promise) {
|
||
|
const prevBatch = updateBatchRef.value;
|
||
|
// const prevState = stateRef.value;
|
||
|
updateBatchRef.value = [];
|
||
|
|
||
|
prevBatch.forEach(batchUpdater => {
|
||
|
stateRef.value = batchUpdater(stateRef.value as State) as UnwrapRef<State>;
|
||
|
});
|
||
|
|
||
|
lastPromiseRef.value = null;
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
onBeforeUnmount(() => {
|
||
|
lastPromiseRef.value = null;
|
||
|
});
|
||
|
|
||
|
return [stateRef as Ref<State>, setFrameState];
|
||
|
}
|
||
|
|
||
|
/** Lock frame, when frame pass reset the lock. */
|
||
|
export function useTimeoutLock<State>(
|
||
|
defaultState?: State,
|
||
|
): [(state: UnwrapRef<State>) => void, () => UnwrapRef<State> | null] {
|
||
|
const frameRef = ref<State | null>(defaultState || null);
|
||
|
const timeoutRef = ref<number>();
|
||
|
|
||
|
function cleanUp() {
|
||
|
window.clearTimeout(timeoutRef.value);
|
||
|
}
|
||
|
|
||
|
function setState(newState: UnwrapRef<State>) {
|
||
|
frameRef.value = newState;
|
||
|
cleanUp();
|
||
|
|
||
|
timeoutRef.value = window.setTimeout(() => {
|
||
|
frameRef.value = null;
|
||
|
timeoutRef.value = undefined;
|
||
|
}, 100);
|
||
|
}
|
||
|
|
||
|
function getState() {
|
||
|
return frameRef.value;
|
||
|
}
|
||
|
|
||
|
onBeforeUnmount(() => {
|
||
|
cleanUp();
|
||
|
});
|
||
|
|
||
|
return [setState, getState];
|
||
|
}
|