Skip to content

Commit ed8f4e8

Browse files
luisherranzDAreRodz
andcommitted
iAPI: Fix using getServerContext in derived state getters (#73518)
* Add failing test for context * Fix test * Add changelog * Don't trigger invalidations twice in the navigation phase * Debounce navigation signal invalidation to prevent redundant updates within a microtask * Revert "Debounce navigation signal invalidation to prevent redundant updates within a microtask" This reverts commit c2bf701. * Use a different navigation signal for context Co-authored-by: luisherranz <[email protected]> Co-authored-by: DAreRodz <[email protected]>
1 parent f34ada7 commit ed8f4e8

File tree

7 files changed

+37
-5
lines changed

7 files changed

+37
-5
lines changed

packages/e2e-tests/plugins/interactive-blocks/get-server-context/render.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
<div data-testid="nonChanging" data-wp-text="context.nonChanging"></div>
5656
<div data-testid="onlyInMain" data-wp-text="context.onlyInMain"></div>
5757
<div data-testid="onlyInModified" data-wp-text="context.onlyInModified"></div>
58+
<div data-testid="serverProp" data-wp-text="state.serverProp"></div>
5859

5960
<button
6061
data-testid="tryToModifyServerContext"

packages/e2e-tests/plugins/interactive-blocks/get-server-context/view.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ import {
99
} from '@wordpress/interactivity';
1010

1111
store( 'test/get-server-context', {
12+
state: {
13+
get serverProp() {
14+
return getServerContext().prop;
15+
},
16+
},
1217
actions: {
1318
navigate: withSyncEvent( function* ( e ) {
1419
e.preventDefault();

packages/interactivity/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### Bug Fixes
66

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

1011
## 6.33.0 (2025-10-17)

packages/interactivity/src/directives.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
type VNode,
1111
type RefObject,
1212
} from 'preact';
13-
import { useContext, useMemo, useRef } from 'preact/hooks';
13+
import { useContext, useLayoutEffect, useMemo, useRef } from 'preact/hooks';
1414
import { signal, type Signal } from '@preact/signals';
1515

1616
/**
@@ -33,7 +33,7 @@ import {
3333
type DirectiveCallback,
3434
type DirectiveEntry,
3535
} from './hooks';
36-
import { getScope } from './scopes';
36+
import { getScope, navigationContextSignal } from './scopes';
3737
import { proxifyState, proxifyContext, deepMerge } from './proxies';
3838
import { PENDING_GETTER } from './proxies/state';
3939

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

1028+
// Triggers an invalidation after the directive data-wp-context has
1029+
// been evaluated and the value of the server context has changed.
1030+
useLayoutEffect( () => {
1031+
if ( vdom && typeof vdom.type !== 'string' ) {
1032+
navigationContextSignal.value =
1033+
navigationContextSignal.peek() + 1;
1034+
}
1035+
}, [ vdom ] );
1036+
10281037
if ( vdom && typeof vdom.type !== 'string' ) {
10291038
// The scope needs to be injected.
10301039
const previousScope = getScope();

packages/interactivity/src/scopes.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
* External dependencies
33
*/
44
import type { h as createElement, RefObject } from 'preact';
5+
import { signal } from '@preact/signals';
56

67
/**
78
* Internal dependencies
89
*/
910
import { getNamespace } from './namespaces';
10-
import { deepReadOnly, navigationSignal, deepClone } from './utils';
11+
import { deepReadOnly, deepClone } from './utils';
1112
import type { Evaluate } from './hooks';
1213

1314
export interface Scope {
@@ -82,6 +83,8 @@ export const getElement = () => {
8283
} );
8384
};
8485

86+
export const navigationContextSignal = signal( 0 );
87+
8588
/**
8689
* Gets the context defined and updated from the server.
8790
*
@@ -125,7 +128,7 @@ export function getServerContext< T extends object >( namespace?: string ): T {
125128

126129
// Accesses the signal to make this reactive. It assigns it to `subscribe`
127130
// to prevent the JavaScript minifier from removing this line.
128-
getServerContext.subscribe = navigationSignal.value;
131+
getServerContext.subscribe = navigationContextSignal.value;
129132

130133
return deepClone( scope.serverContext[ namespace || getNamespace() ] );
131134
}

packages/interactivity/src/store.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,6 @@ export const populateServerData = ( data?: {
328328
}
329329
);
330330
}
331-
navigationSignal.value += 1; // Triggers invalidations.
332331
};
333332

334333
// Parse and populate the initial state and config.

test/e2e/specs/interactivity/get-sever-context.spec.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,4 +259,18 @@ test.describe( 'getServerContext()', () => {
259259
await expect( prop ).toBeEmpty();
260260
await expect( nestedProp ).toBeEmpty();
261261
} );
262+
263+
test( 'should get server context using derived state getters', async ( {
264+
page,
265+
} ) => {
266+
const serverProp = page.getByTestId( 'serverProp' );
267+
268+
await expect( page ).toHaveTitle( /main/ );
269+
await expect( serverProp ).toHaveText( 'child' );
270+
271+
await page.getByTestId( 'modified' ).click();
272+
await expect( page ).toHaveTitle( /modified/ );
273+
274+
await expect( serverProp ).toHaveText( 'childModified' );
275+
} );
262276
} );

0 commit comments

Comments
 (0)