From 60a89f2a4c625ebd3deae4b6fcffa8f632770a53 Mon Sep 17 00:00:00 2001 From: Mathis Fullriede Date: Tue, 19 Nov 2024 13:42:10 +0100 Subject: [PATCH 1/4] feat: add option to refresh on visibilitychange event --- src/lib/useOnVisibilityChange.ts | 18 +++++++++++++ src/resource/types.ts | 1 + src/resource/useWatchResourceValue.test.tsx | 29 +++++++++++++++++++++ src/resource/useWatchResourceValue.ts | 8 ++++++ 4 files changed, 56 insertions(+) create mode 100644 src/lib/useOnVisibilityChange.ts diff --git a/src/lib/useOnVisibilityChange.ts b/src/lib/useOnVisibilityChange.ts new file mode 100644 index 0000000..e3379da --- /dev/null +++ b/src/lib/useOnVisibilityChange.ts @@ -0,0 +1,18 @@ +import { DependencyList, useEffect } from "react"; +import { isBrowser } from "browser-or-node"; + +type Callback = () => void; + +export const useOnVisibilityChange = ( + cb: Callback, + deps: DependencyList, +): void => { + useEffect(() => { + if (isBrowser) { + document.addEventListener("visibilitychange", cb); + return () => { + document.removeEventListener("visibilitychange", cb); + }; + } + }, deps); +}; diff --git a/src/resource/types.ts b/src/resource/types.ts index 8bd6015..b483673 100644 --- a/src/resource/types.ts +++ b/src/resource/types.ts @@ -24,6 +24,7 @@ export type UseWatchResourceOptions = { useSuspense?: boolean; autoRefresh?: DurationLikeObject; refreshOnWindowFocus?: boolean; + refreshOnVisibilityChange?: boolean; } & GetAsyncResourceOptions; export type NoSuspenseReturnType = Readonly< diff --git a/src/resource/useWatchResourceValue.test.tsx b/src/resource/useWatchResourceValue.test.tsx index 9d942cd..17b3400 100644 --- a/src/resource/useWatchResourceValue.test.tsx +++ b/src/resource/useWatchResourceValue.test.tsx @@ -119,6 +119,35 @@ test("focus event does not trigger resource refresh, if 'refreshOnWindowFocus' i expectValue("Foo"); }); +test("visibilitychange event triggers resource refresh, if 'refreshOnVisibilityChange' is enabled", async () => { + options.refreshOnVisibilityChange = true; + render(); + await waitToBeLoaded(); + expectValue("Foo"); + + getName.mockReturnValue("Bar"); + act(() => { + document.dispatchEvent(new Event("visibilitychange")); + }); + + await waitToBeLoaded(); + expectValue("Bar"); +}); + +test("visibilitychange event does not trigger resource refresh, if 'refreshOnVisibilityChange' is not enabled", async () => { + render(); + await waitToBeLoaded(); + expectValue("Foo"); + + getName.mockReturnValue("Bar"); + act(() => { + document.dispatchEvent(new Event("visibilitychange")); + }); + + await waitToBeLoaded(); + expectValue("Foo"); +}); + describe("with disabled suspense", () => { beforeEach(() => { options.useSuspense = false; diff --git a/src/resource/useWatchResourceValue.ts b/src/resource/useWatchResourceValue.ts index a4487f1..1d7b1dc 100644 --- a/src/resource/useWatchResourceValue.ts +++ b/src/resource/useWatchResourceValue.ts @@ -4,6 +4,7 @@ import { useWatchObservableValue } from "../observable-value/useWatchObservableV import { UseWatchResourceOptions, UseWatchResourceResult } from "./types.js"; import { hash } from "object-code"; import { useOnWindowFocused } from "../lib/useOnWindowFocused.js"; +import { useOnVisibilityChange } from "../lib/useOnVisibilityChange.js"; export const useWatchResourceValue = < T, @@ -18,6 +19,7 @@ export const useWatchResourceValue = < keepValueWhileLoading = true, useSuspense = true, refreshOnWindowFocus = false, + refreshOnVisibilityChange = false, autoRefresh, } = options; @@ -37,6 +39,12 @@ export const useWatchResourceValue = < } }, [resource, refreshOnWindowFocus]); + useOnVisibilityChange(() => { + if (refreshOnVisibilityChange && document.visibilityState === "visible") { + resource.refresh(); + } + }, [resource, refreshOnVisibilityChange]); + setTimeout(() => { void resource.load(); }, 0); From de4a0946d172a9ed6339238e52ae110c65c00233 Mon Sep 17 00:00:00 2001 From: mathis <37186532+maaaathis@users.noreply.github.com> Date: Tue, 19 Nov 2024 13:51:55 +0100 Subject: [PATCH 2/4] depend default refreshOnVisibilityChange from refreshOnWindowFocus Co-authored-by: Marco Falkenberg --- src/resource/useWatchResourceValue.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resource/useWatchResourceValue.ts b/src/resource/useWatchResourceValue.ts index 1d7b1dc..670b203 100644 --- a/src/resource/useWatchResourceValue.ts +++ b/src/resource/useWatchResourceValue.ts @@ -19,7 +19,7 @@ export const useWatchResourceValue = < keepValueWhileLoading = true, useSuspense = true, refreshOnWindowFocus = false, - refreshOnVisibilityChange = false, + refreshOnVisibilityChange = refreshOnWindowFocus, autoRefresh, } = options; From 7b0a08a5b885121e2fb5c4406c9aa6bbb5e93950 Mon Sep 17 00:00:00 2001 From: Mathis Fullriede Date: Tue, 19 Nov 2024 14:00:05 +0100 Subject: [PATCH 3/4] return isVisible via callback --- src/lib/useOnVisibilityChange.ts | 8 +++++--- src/resource/useWatchResourceValue.ts | 13 ++++++++----- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/lib/useOnVisibilityChange.ts b/src/lib/useOnVisibilityChange.ts index e3379da..a09febc 100644 --- a/src/lib/useOnVisibilityChange.ts +++ b/src/lib/useOnVisibilityChange.ts @@ -1,7 +1,7 @@ import { DependencyList, useEffect } from "react"; import { isBrowser } from "browser-or-node"; -type Callback = () => void; +type Callback = (isVisible: boolean) => void; export const useOnVisibilityChange = ( cb: Callback, @@ -9,9 +9,11 @@ export const useOnVisibilityChange = ( ): void => { useEffect(() => { if (isBrowser) { - document.addEventListener("visibilitychange", cb); + document.addEventListener("visibilitychange", () => cb(!document.hidden)); return () => { - document.removeEventListener("visibilitychange", cb); + document.removeEventListener("visibilitychange", () => + cb(!document.hidden), + ); }; } }, deps); diff --git a/src/resource/useWatchResourceValue.ts b/src/resource/useWatchResourceValue.ts index 670b203..b443921 100644 --- a/src/resource/useWatchResourceValue.ts +++ b/src/resource/useWatchResourceValue.ts @@ -39,11 +39,14 @@ export const useWatchResourceValue = < } }, [resource, refreshOnWindowFocus]); - useOnVisibilityChange(() => { - if (refreshOnVisibilityChange && document.visibilityState === "visible") { - resource.refresh(); - } - }, [resource, refreshOnVisibilityChange]); + useOnVisibilityChange( + (isVisible) => { + if (refreshOnVisibilityChange && isVisible) { + resource.refresh(); + } + }, + [resource, refreshOnVisibilityChange], + ); setTimeout(() => { void resource.load(); From 285099b86dff1f9a44a4b1f8ceb5788016cf3c61 Mon Sep 17 00:00:00 2001 From: Mathis Fullriede Date: Wed, 20 Nov 2024 08:59:40 +0100 Subject: [PATCH 4/4] rename refreshOnVisibilityChange flag to refreshOnDocumentVisibilityChange --- src/resource/types.ts | 2 +- src/resource/useWatchResourceValue.test.tsx | 4 ++-- src/resource/useWatchResourceValue.ts | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/resource/types.ts b/src/resource/types.ts index b483673..96042ce 100644 --- a/src/resource/types.ts +++ b/src/resource/types.ts @@ -24,7 +24,7 @@ export type UseWatchResourceOptions = { useSuspense?: boolean; autoRefresh?: DurationLikeObject; refreshOnWindowFocus?: boolean; - refreshOnVisibilityChange?: boolean; + refreshOnDocumentVisibilityChange?: boolean; } & GetAsyncResourceOptions; export type NoSuspenseReturnType = Readonly< diff --git a/src/resource/useWatchResourceValue.test.tsx b/src/resource/useWatchResourceValue.test.tsx index 17b3400..e9f34cc 100644 --- a/src/resource/useWatchResourceValue.test.tsx +++ b/src/resource/useWatchResourceValue.test.tsx @@ -119,8 +119,8 @@ test("focus event does not trigger resource refresh, if 'refreshOnWindowFocus' i expectValue("Foo"); }); -test("visibilitychange event triggers resource refresh, if 'refreshOnVisibilityChange' is enabled", async () => { - options.refreshOnVisibilityChange = true; +test("visibilitychange event triggers resource refresh, if 'refreshOnDocumentVisibilityChange' is enabled", async () => { + options.refreshOnDocumentVisibilityChange = true; render(); await waitToBeLoaded(); expectValue("Foo"); diff --git a/src/resource/useWatchResourceValue.ts b/src/resource/useWatchResourceValue.ts index b443921..57bb5cc 100644 --- a/src/resource/useWatchResourceValue.ts +++ b/src/resource/useWatchResourceValue.ts @@ -19,7 +19,7 @@ export const useWatchResourceValue = < keepValueWhileLoading = true, useSuspense = true, refreshOnWindowFocus = false, - refreshOnVisibilityChange = refreshOnWindowFocus, + refreshOnDocumentVisibilityChange = refreshOnWindowFocus, autoRefresh, } = options; @@ -41,11 +41,11 @@ export const useWatchResourceValue = < useOnVisibilityChange( (isVisible) => { - if (refreshOnVisibilityChange && isVisible) { + if (refreshOnDocumentVisibilityChange && isVisible) { resource.refresh(); } }, - [resource, refreshOnVisibilityChange], + [resource, refreshOnDocumentVisibilityChange], ); setTimeout(() => {