From 2a9586bc8d75d0f2d95b04c9b5628eb9627445ee Mon Sep 17 00:00:00 2001 From: dangreen Date: Tue, 8 Sep 2026 20:28:31 +0400 Subject: [PATCH] fix(preact): reuse the parent context by default in `HydrationProvider`, as React does `@nano_kit/react` switched the `reuse` prop of `HydrationProvider` to `true` by default when nested hydration boundaries arrived, so a nested provider renders its children in the parent context. The Preact port kept `reuse` undefined, so the same tree created an isolated child context there, and the two adapters documented different defaults for the same prop. Preact now defaults to `true` as well; pass `reuse={false}` to keep a child context. --- packages/preact/src/hydration.spec.tsx | 42 +++++++++++++++++++ packages/preact/src/hydration.tsx | 3 +- .../docs/store-integrations/preact.mdx | 2 +- 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/packages/preact/src/hydration.spec.tsx b/packages/preact/src/hydration.spec.tsx index 8380126c..646390f4 100644 --- a/packages/preact/src/hydration.spec.tsx +++ b/packages/preact/src/hydration.spec.tsx @@ -8,11 +8,13 @@ import { act } from '@testing-library/preact' import { + type InjectionContext, signal, hydratable } from '@nano_kit/store' import { InjectionContextProvider, + useInjectionContext, useInject, useSignal } from './core.js' @@ -36,6 +38,12 @@ function Test() { ) } +function Context({ contexts }: { contexts: (InjectionContext | undefined)[] }) { + contexts.push(useInjectionContext()) + + return null +} + describe('preact', () => { describe('hydration', () => { describe('HydrationProvider', () => { @@ -86,6 +94,40 @@ describe('preact', () => { expect(container.innerHTML).toBe('
empty
') }) + + it('should reuse the parent hydration context by default', () => { + const contexts: (InjectionContext | undefined)[] = [] + + render( + + + + + + + + + ) + + expect(contexts[1]).toBe(contexts[0]) + }) + + it('should create a child context when reuse is disabled', () => { + const contexts: (InjectionContext | undefined)[] = [] + + render( + + + + + + + + + ) + + expect(contexts[1]).not.toBe(contexts[0]) + }) }) describe('StaticHydrationProvider', () => { diff --git a/packages/preact/src/hydration.tsx b/packages/preact/src/hydration.tsx index 39c4751d..52f743b8 100644 --- a/packages/preact/src/hydration.tsx +++ b/packages/preact/src/hydration.tsx @@ -25,6 +25,7 @@ export interface HydrationProviderProps extends InjectionContextProps { context?: InjectionProvider[] /** * Whether to reuse an existing InjectionContext or create a new one. + * `true` by default. */ reuse?: boolean } @@ -36,7 +37,7 @@ export interface HydrationProviderProps extends InjectionContextProps { export function HydrationProvider({ dehydrated, context = [], - reuse, + reuse = true, children }: HydrationProviderProps) { const currentContext = useInjectionContext() diff --git a/website/src/content/docs/store-integrations/preact.mdx b/website/src/content/docs/store-integrations/preact.mdx index 7ae0837b..6b38e75c 100644 --- a/website/src/content/docs/store-integrations/preact.mdx +++ b/website/src/content/docs/store-integrations/preact.mdx @@ -169,7 +169,7 @@ function App({ dehydrated }) { Props: - `dehydrated?` — dehydrated key-value pairs `[string, unknown][]`, or a falsy value to skip hydration - `context?` — additional injection providers -- `reuse?` — reuse an existing `InjectionContext` instead of creating a new one; set to `true` if you want the hydration context to be shared with the rest of your app instead of isolated +- `reuse?` — reuse an existing `InjectionContext` instead of creating a new one; `true` by default #### `StaticHydrationProvider`