Open thetarnav opened 1 year ago
Definitely worth adding, imo.
There's another related primitive that looks like it's kinda necessary, suspended
, which basically tells you when entering and exiting suspense. That's useful because under an active suspense boundary side effects should be paused, but side effects and "createEffect" instances are not the same thing, "createEffect" merely wraps a side effect, it's not one.
For example in the code below the actual side effect is the "fn" function, suspending the "createEffect" will do nothing to the actual side effect, this requires some manual pausing/resuming of the interval.
createEffect ( () => {
const intervalId = createInterval ( fn, 100 );
onCleanup ( () => {
clearInterval ( intervalId );
});
});
Yeah Ive written about this a bit here: https://hackmd.io/NDrB5kP2QjGUrwYo2DwQ0w
I don't think Suspense is the lowest primitive here. It has very specific behaviors. Behaviors I don't think should change really, but it isn't the only problem of its kind.
That being said transition group issue sounds like I made a poor implementation. Likely the only one available to me at the time. I do like that bounded flushing also seems like the solution here.
Ultimately I think we solve this, but as something independent of Suspense. More fundamental.
I think this issue might be related, since its also about the timing of Suspense vs transition-group: https://github.com/solidjs-community/solid-transition-group/issues/38
There seems to be another interesting component that this would unlock: one that doesn't render a fallback branch at all, it just suspends children, which makes the unsuspension cheaper, potentially by a lot.
I am porting react's swr
library to solid solid-swr
and without createResource I can't add suspense support to my hooks.
Just want to mention that this would help me add suspense support to solid-swr
👍
If anybody is interested https://github.com/Tronikelis/solid-swr
This is how I am currently abusing the createResource
hook to enable suspense for my library, it works but feels like such a hack though:
/**
* monkey-patches the `createResource` solid hook to work with `useSWR`
*/
const useSWRSuspense: typeof useSWR = (key, options) => {
const swr = useSWR(key, options);
let resolveResource: undefined | ((val?: never) => void);
const [resource] = createResource(() => {
return new Promise<undefined>(r => {
resolveResource = r;
});
});
// not in an createEffect, because suspense disables them
(async () => {
await swr._effect(); // this never throws
resolveResource?.();
})().catch(noop);
const dataWithResource = () => {
resource();
return swr.data();
};
return {
...swr,
data: dataWithResource,
};
};
export default useSWRSuspense;
A low-level suspense primitive would be useful for libraries to pause effects in "offscreen" branches.
Currently
Suspense
is not only a component-only primitive, but it is tied to resources, and by extend to routing, SSR, transitions, etc. It also assumes needing a fallback branch, which is not always needed. Because of that it cannot be freely used in libraries, without affecting the rest of the app as a side effect.For example in transition libraries like
motionone
andsolid-transition-group
, when we use<Transition>
withmode="out-in"
, we need to wait for the previous element finish his exit animation, before the newly rendered element can be added to the DOM. But solid doesn't know that the new element is only kept in memory, and not yet appeared on the page, so the updates queue will proceed normally, calling allonMount
callbacks, where we expect to deal with elements connected to the DOM. An issue with more details: https://github.com/solidjs-community/solid-transition-group/issues/34Also if we wish to keep some roots in memory—e.g. to avoid recreating the same elements when filtering a large array, or displaying search highlights, or implementing a root pool primitive—there is no way to simply prevent then from running some side effects.
If we tried to use
<Suspense>
to suspend those branches, any resource read under it will also trigger it, possibly breaking the intended behavior of the app—by not showing a fallback in an expected place, or causing a transition (transaction) to be exited sooner (<Transition>
is commonly used for wrapping rendered routes).Code example `createSuspense` using `Suspense`: https://playground.solidjs.com/anonymous/21cef751-8f37-4354-8a63-0aa475d54e64
```ts function createSuspenseIn @fabiospampinato's
oby
this is solved by having a low-levelsuspense
primitive that simply takes a function to suspend and a boolean signal to inform if the branch should be suspended or not, and returns the value directly, similar tocreateRoot
. When the condition signal istrue
, all effects will be suspended, while resources ignore it and keep the lookup for Suspense they can trigger.Code Something like this could *maybe* be implemented currently as this: (although I'm not sure if resources won't trigger it anyway)
```ts function suspense