Is My Understanding Of RouteSectionProps Correct?

I’ve been trying to implement the cache, load, and createAsync pattern on all of my pages that load data. My goal is to load data on hover before navigating to the page. When the cache function does not need a route parameter, this pattern works as expected. BUT, I struggled to get this pattern working when I needed to pass my cache function a route param. After reading through the docs, I "thought" that I should use the useParams() primitive to get the route params, then pass that route param to the function inside of createAsync(). That didn't work. I dug through the HackerNews example and noticed the use of RouteSectionProps. With RouteSectionProps it does work. So... I want to make sure I understand WHY these two examples differ. When using useParams() I'm guessing that you only get access to the route params AFTER the component loads... meaning the params aren't available on hover. But, when using RouteSectionProps it appears that you get access to the route params BEFORE the component loads... which is why load on hover works. Am I correct? Here's my final implementation.
const getUser = cache(async (email: string): Promise<User> => {
"use server";
const { data, error } = await supabase
.from("users")
.select()
.eq("customer_email", email)
.single();

if (error) {
console.log(error.message);
throw error;
}

return data as User;
}, "user");

export const route: RouteDefinition = {
load({ params }) {
void getUser(params.email);
},
};

export default function UserDetailsPage(props: RouteSectionProps) {
const user = createAsync(() => getUser(props.params.email));
...
}
const getUser = cache(async (email: string): Promise<User> => {
"use server";
const { data, error } = await supabase
.from("users")
.select()
.eq("customer_email", email)
.single();

if (error) {
console.log(error.message);
throw error;
}

return data as User;
}, "user");

export const route: RouteDefinition = {
load({ params }) {
void getUser(params.email);
},
};

export default function UserDetailsPage(props: RouteSectionProps) {
const user = createAsync(() => getUser(props.params.email));
...
}
6 Replies
peerreynders
peerreynders6mo ago
That didn't work.
It would be surprising if inside a component useParams() and RouteSectionProps.params had different values, i.e. it shouldn't matter. In a route component I would always use RouteSectionProps.params; I see useParams() more for nested components to avoid prop drilling. The key to preloading is using the load function to warm the cache points that the page is known to depend on. That way, by the time the components are created (and the createAsyncs run), the requests are already in flight or have already completed.
ChrisThornham
ChrisThornhamOP6mo ago
You're right. The values are the same, but I don't think the load function knows anything about the route params when using useParams. For example, in the example below, getUser inside of createAsync gets the correct value. But I don't think the load function has access to that value. Am I right?
export const route = {
load() {
void getUser(what would go here?);
},
};

export default function UserDetailsPage() {
const params = useParams();
const user = createAsync(() => getUser(params.email));
export const route = {
load() {
void getUser(what would go here?);
},
};

export default function UserDetailsPage() {
const params = useParams();
const user = createAsync(() => getUser(params.email));
peerreynders
peerreynders6mo ago
I view the load() function as "mundane" and "synchronous". So I wouldn't use await in it, return anything from it (even though it states that it would be passed to the route component but in most cases all you have is a promise anyway), or use anything reactive in it. So the parameters are all there is.
ChrisThornham
ChrisThornhamOP6mo ago
Thank you. That was a big mental model for me to understand. I finally get it.
Madaxen86
Madaxen866mo ago
I view the load function as the "warmup" for the cache. The createAsync function inside the component will then get the prefechted data from the cache. And I never use the props returned to the page, instead I always use the useParams,... helpers, so I am always free to move parts of the page to their own component without the need to pass props around. So basically my pages look like this:
const getData = cache(async (id:string) => {
'use server';
//fetch and return data
}, 'getData');

export const route = {
load: ({ params }) => getData(params.id),
} satisfies RouteDefinition;

export default function PageComponent() {
const params = useParams();
const data = createAsync(() => getData(params.id));

return (
<Suspense>
<pre>{JSON.stringify(data()}</pre>
</Suspense>
);
}
const getData = cache(async (id:string) => {
'use server';
//fetch and return data
}, 'getData');

export const route = {
load: ({ params }) => getData(params.id),
} satisfies RouteDefinition;

export default function PageComponent() {
const params = useParams();
const data = createAsync(() => getData(params.id));

return (
<Suspense>
<pre>{JSON.stringify(data()}</pre>
</Suspense>
);
}
ChrisThornham
ChrisThornhamOP6mo ago
Thanks! Those are some great points.
Want results from more Discord servers?
Add your server