diff --git a/.changeset/resize-observer-server-owners.md b/.changeset/resize-observer-server-owners.md new file mode 100644 index 000000000..6f0814380 --- /dev/null +++ b/.changeset/resize-observer-server-owners.md @@ -0,0 +1,5 @@ +--- +"@solid-primitives/resize-observer": patch +--- + +Fix Solid 2 hydration owner counting with a transparent resize observer effect and matching window size onSettled calls. diff --git a/packages/resize-observer/src/index.ts b/packages/resize-observer/src/index.ts index 7557c2c36..15493cca7 100644 --- a/packages/resize-observer/src/index.ts +++ b/packages/resize-observer/src/index.ts @@ -1,6 +1,6 @@ import { makeEventListener } from "@solid-primitives/event-listener"; import { createHydratableSingletonRoot } from "@solid-primitives/rootless"; -import { createHydratableStaticStore, createStaticStore } from "@solid-primitives/static-store"; +import { createStaticStore } from "@solid-primitives/static-store"; import { access, asArray, @@ -10,7 +10,7 @@ import { noop, filterNonNullable, } from "@solid-primitives/utils"; -import { type Accessor, createEffect, onCleanup, sharedConfig } from "solid-js"; +import { type Accessor, createEffect, onSettled, onCleanup, sharedConfig } from "solid-js"; import { isServer } from "@solidjs/web"; type ResizeObserverEntryGeneric = ResizeObserverEntry & { readonly target: T }; @@ -98,6 +98,7 @@ export function createResizeObserver( handleDiffArray(refs, prev, observe, unobserve); prev = refs; }, + { transparent: true }, ); } @@ -124,11 +125,14 @@ export function getWindowSize(): Size { * ); */ export function createWindowSize(): Readonly { - if (isServer) { - return WINDOW_SIZE_FALLBACK; - } - const [size, setSize] = createHydratableStaticStore(WINDOW_SIZE_FALLBACK, getWindowSize); - makeEventListener(window, "resize", () => setSize(getWindowSize())); + const [size, setSize] = createStaticStore( + isServer || sharedConfig.hydrating ? WINDOW_SIZE_FALLBACK : getWindowSize(), + ); + if (isServer || sharedConfig.hydrating) + onSettled(() => { + setSize(getWindowSize()); + }); + if (!isServer) makeEventListener(window, "resize", () => setSize(getWindowSize())); return size; } diff --git a/packages/resize-observer/test/hydration.test.tsx b/packages/resize-observer/test/hydration.test.tsx index a8aaf3c42..baacb98be 100644 --- a/packages/resize-observer/test/hydration.test.tsx +++ b/packages/resize-observer/test/hydration.test.tsx @@ -4,7 +4,7 @@ import { type HydrationRoundTripResult, } from "../../../scripts/test-utils/hydration-harness.ts"; -describe("createElementSize hydration", () => { +describe("resize-observer hydration", () => { let result: HydrationRoundTripResult | undefined; const targets = new Set(); const disconnect = vi.fn(() => targets.clear()); @@ -32,6 +32,24 @@ describe("createElementSize hydration", () => { vi.unstubAllGlobals(); }); + it("hydrates resize primitives and their following sibling", async () => { + result = await renderHydrationRoundTrip( + ` +import { createResizeObserver, createWindowSize } from "@solid-primitives/resize-observer"; +export default function App() { + createResizeObserver([], () => {}); + const size = createWindowSize(); + return <>
{size.width}

{ el.dataset.bound = "yes"; }}>after resize

; +} +`, + import.meta.dirname, + ); + expect(result.consoleMessages).toEqual([]); + expect(result.container.querySelector("p")).toBe(result.serverNodes[1]); + expect(result.container.querySelector("p")?.dataset.bound).toBe("yes"); + expect(result.container.querySelector("div")?.textContent).toBe(String(window.innerWidth)); + }); + it.each(["accessor", "element"])( "hydrates the %s target without replacing server nodes", async path => {