mirror of
https://github.com/gethomepage/homepage.git
synced 2026-09-26 22:11:20 -07:00
Chore: full react hooks eslint compliance (#7040)
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
import { useSyncExternalStore } from "react";
|
||||
|
||||
const MINUTE = 60_000;
|
||||
const stores = new Map();
|
||||
|
||||
function getStore(refreshInterval) {
|
||||
const existing = stores.get(refreshInterval);
|
||||
if (existing) return existing;
|
||||
|
||||
const listeners = new Set();
|
||||
let now = null; // null until mounted, so SSR and hydration render the same thing
|
||||
let timer = null;
|
||||
|
||||
const store = {
|
||||
getSnapshot: () => now,
|
||||
subscribe: (onStoreChange) => {
|
||||
listeners.add(onStoreChange);
|
||||
|
||||
if (!timer) {
|
||||
now = Date.now();
|
||||
timer = setInterval(() => {
|
||||
now = Date.now();
|
||||
listeners.forEach((listener) => listener());
|
||||
}, refreshInterval);
|
||||
}
|
||||
|
||||
return () => {
|
||||
listeners.delete(onStoreChange);
|
||||
if (!listeners.size) {
|
||||
clearInterval(timer);
|
||||
timer = null;
|
||||
}
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
stores.set(refreshInterval, store);
|
||||
return store;
|
||||
}
|
||||
|
||||
// Returns null on the server and during hydration, then the current time.
|
||||
export default function useCurrentTime(refreshInterval = MINUTE) {
|
||||
const { subscribe, getSnapshot } = getStore(refreshInterval);
|
||||
return useSyncExternalStore(subscribe, getSnapshot, () => null);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// @vitest-environment jsdom
|
||||
|
||||
import { act, render, screen } from "@testing-library/react";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import useCurrentTime from "./use-current-time";
|
||||
|
||||
function CurrentTime({ refreshInterval }) {
|
||||
return <span>{useCurrentTime(refreshInterval)}</span>;
|
||||
}
|
||||
|
||||
describe("utils/hooks/use-current-time", () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(1_000);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("updates at the requested interval", () => {
|
||||
render(<CurrentTime refreshInterval={5_000} />);
|
||||
expect(screen.getByText("1000")).toBeInTheDocument();
|
||||
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(5_000);
|
||||
});
|
||||
|
||||
expect(screen.getByText("6000")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -1,26 +1,17 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useSyncExternalStore } from "react";
|
||||
|
||||
const hasFocus = () => typeof document !== "undefined" && document.hasFocus();
|
||||
|
||||
const useWindowFocus = () => {
|
||||
const [focused, setFocused] = useState(hasFocus);
|
||||
const subscribe = (onFocusChange) => {
|
||||
window.addEventListener("focus", onFocusChange);
|
||||
window.addEventListener("blur", onFocusChange);
|
||||
|
||||
useEffect(() => {
|
||||
setFocused(hasFocus());
|
||||
|
||||
const onFocus = () => setFocused(true);
|
||||
const onBlur = () => setFocused(false);
|
||||
|
||||
window.addEventListener("focus", onFocus);
|
||||
window.addEventListener("blur", onBlur);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("focus", onFocus);
|
||||
window.removeEventListener("blur", onBlur);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return focused;
|
||||
return () => {
|
||||
window.removeEventListener("focus", onFocusChange);
|
||||
window.removeEventListener("blur", onFocusChange);
|
||||
};
|
||||
};
|
||||
|
||||
const useWindowFocus = () => useSyncExternalStore(subscribe, hasFocus, () => false);
|
||||
|
||||
export default useWindowFocus;
|
||||
|
||||
@@ -12,15 +12,17 @@ function Fixture() {
|
||||
|
||||
describe("utils/hooks/window-focus", () => {
|
||||
it("tracks focus/blur events", async () => {
|
||||
vi.spyOn(document, "hasFocus").mockReturnValue(true);
|
||||
const hasFocus = vi.spyOn(document, "hasFocus").mockReturnValue(true);
|
||||
|
||||
render(<Fixture />);
|
||||
|
||||
expect(screen.getByTestId("focused")).toHaveTextContent("true");
|
||||
|
||||
hasFocus.mockReturnValue(false);
|
||||
window.dispatchEvent(new Event("blur"));
|
||||
await waitFor(() => expect(screen.getByTestId("focused")).toHaveTextContent("false"));
|
||||
|
||||
hasFocus.mockReturnValue(true);
|
||||
window.dispatchEvent(new Event("focus"));
|
||||
await waitFor(() => expect(screen.getByTestId("focused")).toHaveTextContent("true"));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user