Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
<div data-testid="nonChanging" data-wp-text="context.nonChanging"></div>
<div data-testid="onlyInMain" data-wp-text="context.onlyInMain"></div>
<div data-testid="onlyInModified" data-wp-text="context.onlyInModified"></div>
<div data-testid="serverProp" data-wp-text="state.serverProp"></div>

<button
data-testid="tryToModifyServerContext"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ import {
} from '@wordpress/interactivity';

store( 'test/get-server-context', {
state: {
get serverProp() {
return getServerContext().prop;
},
},
actions: {
navigate: withSyncEvent( function* ( e ) {
e.preventDefault();
Expand Down
1 change: 1 addition & 0 deletions packages/interactivity/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Bug Fixes

- Return a deep-clone object from `getServerState` and `getServerContext` functions. ([#73437](https://github.com/WordPress/gutenberg/pull/73437))
- Fix using `getServerContext` in derived state getters. ([#73518](https://github.com/WordPress/gutenberg/pull/73518))
- Fix derived state closures processing on client-side navigation. ([#72725](https://github.com/WordPress/gutenberg/pull/72725))

## 6.33.0 (2025-10-17)
Expand Down
13 changes: 11 additions & 2 deletions packages/interactivity/src/directives.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
type VNode,
type RefObject,
} from 'preact';
import { useContext, useMemo, useRef } from 'preact/hooks';
import { useContext, useLayoutEffect, useMemo, useRef } from 'preact/hooks';
import { signal, type Signal } from '@preact/signals';

/**
Expand All @@ -33,7 +33,7 @@ import {
type DirectiveCallback,
type DirectiveEntry,
} from './hooks';
import { getScope } from './scopes';
import { getScope, navigationContextSignal } from './scopes';
import { proxifyState, proxifyContext, deepMerge } from './proxies';
import { PENDING_GETTER } from './proxies/state';

Expand Down Expand Up @@ -1025,6 +1025,15 @@ export default () => {
// Get the content of this router region.
const vdom = routerRegions.get( regionId )!.value;

// Triggers an invalidation after the directive data-wp-context has
// been evaluated and the value of the server context has changed.
useLayoutEffect( () => {
if ( vdom && typeof vdom.type !== 'string' ) {
navigationContextSignal.value =
navigationContextSignal.peek() + 1;
}
}, [ vdom ] );

if ( vdom && typeof vdom.type !== 'string' ) {
// The scope needs to be injected.
const previousScope = getScope();
Expand Down
7 changes: 5 additions & 2 deletions packages/interactivity/src/scopes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
* External dependencies
*/
import type { h as createElement, RefObject } from 'preact';
import { signal } from '@preact/signals';

/**
* Internal dependencies
*/
import { getNamespace } from './namespaces';
import { deepReadOnly, navigationSignal, deepClone } from './utils';
import { deepReadOnly, deepClone } from './utils';
import type { Evaluate } from './hooks';

export interface Scope {
Expand Down Expand Up @@ -82,6 +83,8 @@ export const getElement = () => {
} );
};

export const navigationContextSignal = signal( 0 );

/**
* Gets the context defined and updated from the server.
*
Expand Down Expand Up @@ -125,7 +128,7 @@ export function getServerContext< T extends object >( namespace?: string ): T {

// Accesses the signal to make this reactive. It assigns it to `subscribe`
// to prevent the JavaScript minifier from removing this line.
getServerContext.subscribe = navigationSignal.value;
getServerContext.subscribe = navigationContextSignal.value;

return deepClone( scope.serverContext[ namespace || getNamespace() ] );
}
Expand Down
1 change: 0 additions & 1 deletion packages/interactivity/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,6 @@ export const populateServerData = ( data?: {
}
);
}
navigationSignal.value += 1; // Triggers invalidations.
};

// Parse and populate the initial state and config.
Expand Down
14 changes: 14 additions & 0 deletions test/e2e/specs/interactivity/get-sever-context.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,4 +259,18 @@ test.describe( 'getServerContext()', () => {
await expect( prop ).toBeEmpty();
await expect( nestedProp ).toBeEmpty();
} );

test( 'should get server context using derived state getters', async ( {
page,
} ) => {
const serverProp = page.getByTestId( 'serverProp' );

await expect( page ).toHaveTitle( /main/ );
await expect( serverProp ).toHaveText( 'child' );

await page.getByTestId( 'modified' ).click();
await expect( page ).toHaveTitle( /modified/ );

await expect( serverProp ).toHaveText( 'childModified' );
} );
} );
Loading