S
SolidJS3mo ago
jack

typing Page props ?

is it possible to type the props for the page? I'm tryna to block the page until render, and the best I've come up with is:
const load = cache(
(userId: string) => api.getSelectionsByUserId(userId),
"selections"
);

export const route = {
preload: () => load,
} satisfies RouteDefinition;

export default function Username(props: ManuallyTypePropsAfterLookingAtConsoleLog) {
const selections = createAsync(() => load(users[0].id), {
deferStream: true,
initialValue: props.data,
});
//...
const load = cache(
(userId: string) => api.getSelectionsByUserId(userId),
"selections"
);

export const route = {
preload: () => load,
} satisfies RouteDefinition;

export default function Username(props: ManuallyTypePropsAfterLookingAtConsoleLog) {
const selections = createAsync(() => load(users[0].id), {
deferStream: true,
initialValue: props.data,
});
//...
but the props have to be manually typed
22 Replies
Madaxen86
Madaxen863mo ago
RouteSectionProps But when the server function is cached you don’t need to pass a initial value because the client will get it from the cache when it’s already preloaded.
jack
jack3mo ago
But the signal is possibly undefined which I’m trying to avoid
Madaxen86
Madaxen863mo ago
As it is async data it may be undefined as createAsync does not await. And if ssr is enabled the server might start rendering will start streaming before the promise has resolved. That’s why we can add suspense boundaries which will render a fallback on initial load . But there’s a few things you can do depending on what you want to do. First you can add deferStream:true to createAsync which will prevent streaming until the promise is resolved. Unfortunately typescript won’t pick this up. So you can also add a Show component with keyed prop which will help typescript to narrow the type to the actual resolved type of the promise.
jack
jack3mo ago
Hmm yea I have deferStream here I guess I could just cast the return value to be ignore the | undefined. I don't really want to introduce extra markup when i know the value will be defined two things: - is there a reason TS doesn't pick up on the deferStream? - anything wrong with making something like this for this case:
const createAsyncDeferred = <T,>(
...args: Parameters<typeof createAsync<T>>
) => {
const [fn, opts] = args;
return createAsync<T>(fn, {
...opts,
deferStream: true,
}) as Accessor<T>;
};
const createAsyncDeferred = <T,>(
...args: Parameters<typeof createAsync<T>>
) => {
const [fn, opts] = args;
return createAsync<T>(fn, {
...opts,
deferStream: true,
}) as Accessor<T>;
};
That way it's always defined? Is there a case I'm not considering?
Madaxen86
Madaxen863mo ago
I‘m not deep into the source code of solid. My best guess: Streaming only happens when the server renders the page (directly hit the url or refresh browser) that’s when defer happens. But on client side navigation the signal will still be undefined until resolved
Brendonovich
Brendonovich3mo ago
deferStream stops the SSR response reaching the client until the data has loaded, but it doesn't stop the non-blocking nature of solid's async handling The route's JSX will be rendered while the data is still loading, and then (with deferStream) the server wll update the places that used the data to have the new values, and then send that result to the client The wrong thing with your createAsyncDeferred is that it's a lie - selections will be undefined on the server initially, you can account for that with <Show> to type narrow and remove the undefined, or do optional chaining like selections()?.value
jack
jack3mo ago
So it will only render the fallback ever on server if I use deferStream, essentially ?
Brendonovich
Brendonovich3mo ago
Pretty much, the server will only respond once the data is in place
jack
jack3mo ago
Interesting So if I do my helper thing, realistically nothing can come of this right? TS is not runtime so even if on server first pass there’s some broken markup, it will never reach client until it’s accurate (at which point forcing the signal to be defined is accurate)? Like I get that it’s semantically incorrect bc on server at some point that will be undefined
Brendonovich
Brendonovich3mo ago
TS isn’t runtime but what it’s telling you is important, on the server the value will initially be undefined, and if you don’t respect that then you could end up with ‘cannot access property of undefined’ on the server deferStream pauses the response from being sent but the JSX will still fully evaluate, so you need appropriate guards in place for the undefined case
jack
jack3mo ago
Ok true
Brendonovich
Brendonovich3mo ago
If you use Show so that there’s 0 markup initially that’s fine, since when the response is sent it would have re-evaluated the Show
jack
jack3mo ago
Yea I guess im trying to force a way of writing code into this that just isn’t really possible I just switched off of Astro and so I’m trying to copy like 1:1 over but it’s not matching up different paradigm
Brendonovich
Brendonovich3mo ago
Fwiw this sort of stuff is what solid 2.0 is looking to address, but for now we just have to live with guarding against undefined with async data
jack
jack3mo ago
Ok cool. I feel like a lot of these things are either a. Missing docs or b. Really primitive and missing sensible abstractions That’s the nature of adopting it this early I suppose though
Brendonovich
Brendonovich3mo ago
Do you mean the specific options like deferStream or the bigger picture of loading async data and pleasing typescript?
jack
jack3mo ago
Just like the patterns around these things Like here I have to define like 3 separate functions to get ssr’d data going, as opposed to remix/svelte/old next where I dump everything into an exported function and I have it Also more complex examples would be cool. I have no clue if I’m doing things properly rn
Brendonovich
Brendonovich3mo ago
tbf the route preloader isn’t mandatory but I get your point haha
jack
jack3mo ago
Maybe it’s just a docs thing then I think having everything in one docs is awesome, but also is confusing a lot when it’s jumping me between router and start But I thought that I needed the load/preload for it to work based on docs
Brendonovich
Brendonovich3mo ago
Yeah it’s tricky since technically none of what we’ve discussed has to do with Start but a lot of it gets demoed there Just the file based routing syntax
jack
jack3mo ago
Right right I’ll just start looking at source code to figure things out instead lol
Madaxen86
Madaxen863mo ago
I have to agree. createAsync is part of the router doc which has only this as an example
const user = createAsync(() => getUser(params.id));
const user = createAsync(() => getUser(params.id));
While a more sophisticated example can only be found in the solid-start docs for data loading. But there’s no example showing how to fetch data and also do the mutation/ revalidation of cached functions which just gives a whole picture of how to do it the solid way.
Want results from more Discord servers?
Add your server