-
Notifications
You must be signed in to change notification settings - Fork 451
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
fix(presentation): use live content API for loaders #8429
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
2 Skipped Deployments
|
No changes to documentation |
Component Testing Report Updated Jan 30, 2025 3:34 PM (UTC) ❌ Failed Tests (3) -- expand for details
|
⚡️ Editor Performance ReportUpdated Thu, 30 Jan 2025 15:35:21 GMT
Detailed information🏠 Reference resultThe performance result of
🧪 Experiment resultThe performance result of this branch
📚 Glossary
|
6bdee9b
to
084ad72
Compare
084ad72
to
2cd609d
Compare
2cd609d
to
39127ab
Compare
39127ab
to
91cd5cb
Compare
useEffect(() => { | ||
setOnline(navigator.onLine) | ||
// eslint-disable-next-line @typescript-eslint/no-shadow | ||
const online = () => setOnline(true) | ||
const offline = () => setOnline(false) | ||
window.addEventListener('online', online) | ||
window.addEventListener('offline', offline) | ||
return () => { | ||
window.removeEventListener('online', online) | ||
window.removeEventListener('offline', offline) | ||
} | ||
}, []) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
client.live.events()
handles this internally by emitting a reconnect
event.
// Should pause activity when offline | ||
if (!online) { | ||
return true | ||
} | ||
|
||
// Should pause when the document isn't visible, as it's likely the user isn't looking at the page | ||
if (visibilityState === 'hidden') { | ||
return true | ||
} | ||
|
||
return false | ||
} | ||
|
||
function onVisibilityChange(onStoreChange: () => void): () => void { | ||
document.addEventListener('visibilitychange', onStoreChange) | ||
return () => document.removeEventListener('visibilitychange', onStoreChange) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
client.live.events()
does not handle pausing on visibility hidden, but it's less necessary since we're no longer polling either. If a tab is suspended and brought back we do however handle that automatically.
const onFocus = () => setState('stale') | ||
window.addEventListener('focus', onFocus) | ||
return () => window.removeEventListener('focus', onFocus) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With client.live.events()
there's no longer a need to refetch on window focus 🥳
export function useQueryParams(params?: undefined | null | QueryParams): QueryParams { | ||
const stringifiedParams = useMemo(() => JSON.stringify(params || {}), [params]) | ||
return useMemo(() => JSON.parse(stringifiedParams) as QueryParams, [stringifiedParams]) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The stability of query params are now handled by the useLiveQueries
reducer, we no longer need this hack 😌
@@ -227,7 +227,6 @@ | |||
"lodash": "^4.17.21", | |||
"log-symbols": "^2.2.0", | |||
"mendoza": "^3.0.0", | |||
"mnemonist": "0.39.8", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One less chonky dependency 😮💨
}, []) | ||
|
||
useEffect(() => { | ||
useEffect((): (() => void) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This typing:
useEffect((): (() => void) => {
// ^
})
Is there to avoid easy mistakes, as further down this is returned:
return nextComlink.start()
Calling .start()
returns an unsubscribe handler. This is a bit implicit and easy to miss. Should someone refactor this useEffect later and miss the return
statement then TS will catch it 😮💨
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! In future I'll explicitly call unsubscribe in cleanup functions as I'm definitely guilty of not doing this, and I can see how it could be missed in a refactor.
key={`${key}${perspective}`} | ||
projectId={clientConfig.projectId!} | ||
dataset={clientConfig.dataset!} | ||
key={`${liveEvents.resets}:${key}`} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The liveEvents.resets
ensures that if a reconnect
or restart
live event happens then QuerySubscription
components here start from a clean slate. This helps ensure their internal tracking of what liveEventsMessages
they can ignore, as they happened before the component mounted, and which ones to look for a lastLiveEventId
, works correctly.
91cd5cb
to
9a60bd7
Compare
@@ -10,26 +11,13 @@ export const EDIT_INTENT_MODE = 'presentation' | |||
export const MAX_TIME_TO_OVERLAYS_CONNECTION = 3_000 // ms | |||
|
|||
// The API version to use when using `@sanity/client` | |||
export const API_VERSION = '2023-10-16' | |||
export const API_VERSION = apiVersion |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aligning on the apiVersion
used here increases the potential effect of sanity-io/client#990
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome, this is a great improvement and another great benefit of the LCAPI. It should also help alleviate the fear users have when they inspect the network tab when using Presentation. 😆
Thanks for all the comments, really helpful as usual.
Description
This PR switches Presentation when used to power loader queries (this is what
@sanity/react-loader
'suseLiveMode
is using) from using a aggressive polling refetch approach that fires every two seconds, to Sanity Live Content with Drafts, which ensures refetches only happen when content has actually changed.less.requests.mov
What to review
Hopefully there's enough context to understand the changes 🙌
Testing
New tests added for new state logic that are reducer based. There's still a lack of E2E tests so manual testing is required.
Notes for release
Presentation Tool is now using Sanity Live Content API to drive Live Mode in integrations like
@sanity/react-loader
,@sanity/svelte-loader
,@nuxtjs/sanity
and@sanity/core-loader
.The new setup allows query refetching to only happen when actually needed, as opposed to every two seconds.
Why have we been aggressively fetching every two seconds?
It's in order to ensure changes that can't reasonably be predicted (using Content Source Maps/CSM) client side are still caught, and eventual consistency in the live preview reached:
count(*[_type == "person" && isPublished])
For the client to know when this query count will change it would need to monitor every person document and if
isPublished
is true. That doesn't scale.*[slug.current == "discounted"]
If any document is given the slug
discounted
, the query needs to refetch. The client would need to monitor every single document mutation in the dataset and check if a new document now has the slug, as well as check if any fields on the current matching document has changed, or if the matching document got deleted and there's a fallback match, or none at all.The gist of it is the client can't predict everything, and if it tries, it winds up using a lot of memory and perform work, just in case anything has changed.
While this over-fetching never counted towards your Sanity API hit quota, the intention was to move over to a better architecture.
And that new API is Sanity Live; it's built to solve "when should a query refetch?" and it allows Presentation to rely on the backend for telling it when it needs to refetch, thus using far less memory, less network resources, and less work on the main thread.