Closed Grubba27 closed 1 year ago
I've updated the skipUpdate
func as well. I've opted to maintain its signature as <T>(prev: T, next: T) => boolean
and added await to the parameters like this since we are in an async Tracker.autorun
things should be fine:
!skipUpdate(await refs.trackerData, await data)
FYI, a demo running with the code in this PR is the one below:
What is the cache system for? Meteor.find (and it's collection/pub/sub system in general) already has caching (it basically IS a client side cache). Adding a cache to useFind seems like duplicated effort.
The point is, we need a stable reference to a Promise
we throw (Suspense for data fetching works like this). And as the fetchAsync
creates a new one every single time, we need an additional "cache" here, to map the hook params into the stable reference.
Ah, that's one of the constraints that prevented me from getting something like this working from inside the useFind/useTracker hook (There's a similar issue with the unfinished useMeteorState hook).
But I think that's not the right strategy anyway. I think the better strategy is to hold the reference where it is caught, in a provider. This is similar to how SSR (or static gen) has always worked, except now you'd be using suspense. In this case, your Provider would also be the ErrorBoundary that catches the thrown promise. (If I remember how it all goes together - been a while.)
The challenge comes from nested, derivative calls. This was a challenge to get right in Loadable too.
/2cents
The "provider approach" is not needed here, as the Suspense mechanism already covers that - we can keep the pending states local (i.e., to the first <Suspense>
up in the tree). We could have a provider for the promise map, but that's in implementation detail - it'd still be "global", i.e., accessible everywhere in the tree.
Also, error boundary won't catch a thrown Promise
- that's a special case for the "Suspense for data fetching" to make it work. (It's still experimental though.)
Pointed to useFind with suspense because the ESLint was already configured there.
Still missing a few pieces (it works, but it has some things that need to be solved)
I will list the issue that I think needs to be solved before this goes out:
For testing this change
## client ```ts import { EJSON } from 'meteor/ejson'; import { Meteor } from 'meteor/meteor'; import React, { Fragment, Suspense, useCallback, useEffect, useMemo, useState } from 'react'; import { LinksCollection } from '../api/links'; import isEqual from 'lodash/isEqual'; const cachedSubscriptions: SubscriptionEntry[] = []; type SubscriptionEntry = { params: EJSON[]; name: string; handle?: Meteor.SubscriptionHandle; promise: Promise