-
-
Notifications
You must be signed in to change notification settings - Fork 134
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
feat: Configurable throttling #374
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
I tried with a text input and noticed two problems:
bandicam.2023-10-24.19-08-17-861.mp4In the video above, left input uses |
This is expected. Internally, this behaves much like React.useState, which would re-render when the state changes. If you wish to limit the level of re-rendering, using a
This is probably a cache issue with the RSC client cache, I'll have a look, thanks for the report! |
Ok so the second point is actually a race condition between local edits and the RSC payload coming in that triggers a URL change to match what was rendered on the server. So you type "foo bar". The first throttled request to the server sees and renders "foo", re-updates the local URL to "foo" and " bar" is dropped. Throttling actually makes things worse because all edits between two responses may be lost, and increasing throttling increases the chance of it happening. So it looks like the best approach is indeed to use an uncontrolled component, set the default value to the state value, which will deal with page loads / mounts, and use the |
d1b5abf
to
f86a129
Compare
3a2b352
to
8549de1
Compare
30f9091
to
a6394ac
Compare
After playing for a while with throttling options and how to properly schedule server updates, I don't think there's an ideal setting that can work anywhere anytime. Taking care of the race-condition of a stale external navigation overwriting the hooks internal states was easy once the right cause was identified: the URL update queue is now merged with incoming external search params to sync internal states. However, it looks like RSC streams don't get applied to the DOM if the URL that triggered them is different when they arrive (probably due to the client RSC cache being keyed by the URL). This caused the whole app to be unresponsive when using only the Next.js router for query updates, as the URL would only be updated when the appropriate RSC payload resolved, which may never happen if the network connection is poor (or absent) and/or the serverless function times out on the backend. Instead, query updates now always update the query string locally first (using the Web History API), then notify the server with a For high frequency non-shallow updates (like binding a query state to a text input or a range slider), a higher This behaviour is showcased in the throttling playground, where artificial delays can be configured. Using the browser devtools to throttle the network connection also helps get an idea of how the server responds to floods of requests. |
See discussion #373.
Saves a few bytes on the output bundle.
When the server streams in a shallow: false navigation, it may do so with an outdated querystring if there are updates pending in the queue, and those would overwrite the internal states. So before triggering a sync, we merge the queue onto the incoming search params and sync on the result.
Kind of a stretch, but it gives an escape hatch for high-frequency query updates
This is because relying only on the Next.js router for URL updates makes it dependent on the RSC payload to resolve, which is not the case in some situations: - Offline or poor network connection - Requests waterfalling when updating too quickly Instead, the browser's history API is used for state management, and optionally on non-shallow updates we can tell the server about the updated URL to re-render and stream in RSC payloads.
6fbd8dc
to
44754f9
Compare
🎉 This PR is included in version 1.10.0-beta.1 🎉 The release is available on: Your semantic-release bot 📦🚀 |
🎉 This PR is included in version 1.10.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Adds a
throttleMs
option to slow down the rate of updates to the URL.Note: internal state (the first item returned by the hook) is not throttled, and behaves just like React.useState to guarantee UI reactivity. Only query string updates to the URL are throttled, to handle browsers rate-limiting the history API, and to slow down sending network requests to the server when using
shallow: false
.As an example, to safely bind a high-frequency query state on Safari, which has higher rate limits:
See discussion #373.