perf(tabs): debounce UI state mementos
This commit is contained in:
2 files changed
+28
-5
No files matched your search
+7
-2
@@ -1,8 +1,8 @@
|
||||
/** @vitest-environment happy-dom */
|
||||
|
||||
import { createApp, defineComponent, h, nextTick, ref } from "vue";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { MAX_TAB_PAGE_UI_STATE_BYTES, provideTabUiState, sanitizeTabPageUiState, sanitizeTabUiState, useTabUiState } from "@/lib/tabs/tabUiState";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { MAX_TAB_PAGE_UI_STATE_BYTES, provideTabUiState, sanitizeTabPageUiState, sanitizeTabUiState, TAB_UI_STATE_TRACK_DEBOUNCE_MS, useTabUiState } from "@/lib/tabs/tabUiState";
|
||||
|
||||
const mounted: Array<{ unmount: () => void; host: HTMLElement }> = [];
|
||||
|
||||
@@ -11,10 +11,12 @@ afterEach(() => {
|
||||
unmount();
|
||||
host.remove();
|
||||
}
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
describe("tabUiState", () => {
|
||||
it("restores a namespace and flushes its last value before unmount", async () => {
|
||||
vi.useFakeTimers();
|
||||
const updates: unknown[] = [];
|
||||
const trackedValue = ref(0);
|
||||
let initialValue = 0;
|
||||
@@ -53,6 +55,9 @@ describe("tabUiState", () => {
|
||||
|
||||
trackedValue.value = 8;
|
||||
await nextTick();
|
||||
expect(updates).toHaveLength(0);
|
||||
vi.advanceTimersByTime(TAB_UI_STATE_TRACK_DEBOUNCE_MS);
|
||||
await nextTick();
|
||||
expect(updates.at(-1)).toEqual({ Panel: { value: 8 } });
|
||||
|
||||
trackedValue.value = 9;
|
||||
|
||||
@@ -2,6 +2,7 @@ import { inject, onBeforeUnmount, provide, watch, type InjectionKey } from "vue"
|
||||
import type { TabPageUiState, TabUiState } from "@/types/database";
|
||||
|
||||
export const MAX_TAB_PAGE_UI_STATE_BYTES = 64 * 1024;
|
||||
export const TAB_UI_STATE_TRACK_DEBOUNCE_MS = 100;
|
||||
|
||||
interface TabUiStateContext {
|
||||
snapshot: TabPageUiState;
|
||||
@@ -28,11 +29,28 @@ export function useTabUiState<T extends object>(
|
||||
const update = (patch: Partial<T>) => {
|
||||
if (active) context?.update(namespace ? { [namespace]: patch } : (patch as TabPageUiState));
|
||||
};
|
||||
const track = (snapshot: () => Partial<T>) => {
|
||||
const stop = watch(snapshot, update, { deep: true, flush: "post" });
|
||||
onBeforeUnmount(() => {
|
||||
const track = (snapshot: () => Partial<T>, debounceMs = TAB_UI_STATE_TRACK_DEBOUNCE_MS) => {
|
||||
let timer: ReturnType<typeof setTimeout> | undefined;
|
||||
const flush = () => {
|
||||
if (timer !== undefined) {
|
||||
clearTimeout(timer);
|
||||
timer = undefined;
|
||||
}
|
||||
update(snapshot());
|
||||
};
|
||||
const schedule = () => {
|
||||
if (!active) return;
|
||||
if (timer !== undefined) clearTimeout(timer);
|
||||
if (debounceMs <= 0) {
|
||||
flush();
|
||||
return;
|
||||
}
|
||||
timer = setTimeout(flush, debounceMs);
|
||||
};
|
||||
const stop = watch(snapshot, schedule, { deep: true, flush: "post" });
|
||||
onBeforeUnmount(() => {
|
||||
stop();
|
||||
flush();
|
||||
active = false;
|
||||
});
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user