-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Interactivity API: Add a new API for getting the initial server-side state & context #65243
Comments
@DAreRodz and I have found an issue that needs to be solved in the current design: When using the store( 'example', {
callbacks: {
reset() {
getContext().timer = getServerContext().timer;
}
}
} ); <div
data-wp-watch="callbacks.reset"
...
>
</div> The We might need to find a way to "manually" trigger the observers after the client-side navigation. |
Oh this is fantastic. The quiz example is especially apt as converting our quiz builder plugin to the interactivity api is my next big project. |
@DAreRodz, how did you resolve the issue outlined in #65243 (comment)? |
@gziolo, I've been thinking about this, and maybe there is no problem after all, so I decided not to write any logic to handle this case. Here is my reasoning: For this issue to arise, these conditions must be fulfilled:
In such a case, where developers know that the conditions above are met, they could subscribe to reset() {
store( 'core/router' ).state.url;
getContext().timer = getServerContext().timer;
}, I think that should be enough and would trigger the minimum number of updates. Otherwise, if we decide to force signal updates, every directive reading from This is an example of <script type="module">
import { store, getContext, getServerContext, withScope } from '@wordpress/interactivity';
const { state } = store( 'test/context-timer', {
callbacks: {
start() {
setInterval( withScope( () => {
getContext().timer++;
} ), 1000 );
},
reset() {
store( 'core/router' ).state.url;
getContext().timer = getServerContext().timer;
},
}
} );
</script>
<div
data-wp-interactive="test/context-timer"
data-wp-context='{ "timer": 0 }'
data-wp-init="callbacks.start"
data-wp-watch="callbacks.reset"
data-wp-text="context.timer"
>
</div> The same but for <script type="module">
import { store, getServerState, withScope } from '@wordpress/interactivity';
const { state } = store( 'test/state-timer', {
callbacks: {
start() {
setInterval( withScope( () => {
state.timer++;
} ), 1000 );
},
reset() {
store( 'core/router' ).state.url;
state.timer = getServerState().timer;
},
}
} );
</script>
<div
data-wp-interactive="test/state-timer"
data-wp-init="callbacks.start"
data-wp-watch="callbacks.reset"
data-wp-text="state.timer"
>
</div> |
Oops, I've noticed an issue with the approach I mentioned above. 😅 The That triggers an update even though the For instance, if you import We could fix that by populating the initial URL from the server so it doesn't change when the router module is loaded. Would that make sense? Can we set the same URL we have in the browser from PHP? |
I think that it won't be reliable because the PHP server can sit behind a Cloudflare proxy, etc. So, the URL in PHP can be different from the final URL in the browser. However, I thought about this problem a little more, and I realized that this scenario is pretty unlikely to occur:
Suppose a property X is updated on the client. In that case, a developer should not expect that it will magically be persisted after doing client-side navigation. The developer should know that this property must also be updated on the server (e.g. via a REST API call) to persist after client-side navigations. Of course, we don't have a dedicated API to do server-side mutations; you'd need a custom REST API endpoint for this. But that's another conversation 😎. In conclusion, the current behavior should be OK as is! |
Background
Currently, the Interactivity API does not provide a way to always get the initial server-side state or context. For example,
getContext()
will return the initial context on the initial render but subsequent calls will return the client-side context.This is a conscious design decision, however, sometimes it might be necessary to have access to the server-side context on the new page after client-side navigation.
Example
This example discusses the case of context, but the same issue exists for state.
Imagine an online quiz application. The user is presented with a question on each page. The user has 60 seconds to answer each question. After the user answers a question, they are redirected to the next question on the next page.
The application could look something like this:
When the user navigates to the next question, the
getContext()
call will return the client-side context, which is{"timer": <whatever-time-is-left>}
. This is not what we want! We want the initial server-side context, which is{"timer": 60}
, because the user has 60 seconds to answer each question!The proposal
We propose adding a new API to the Interactivity API to get the initial server-side state or context.
The API would be similar to
getContext()
andgetState()
, but it would return the initial server-side state or context instead of the client-side state or context.Using the quiz application example, the API would be called like this:
getServerContext()
The
getServerContext()
function would return the initial server-side context. It would be called likegetContext()
and typically be used to override the client-side context:getServerState()
The
getServerState()
should function analogously togetServerContext()
, but for state.The text was updated successfully, but these errors were encountered: