How to trigger server function for SSR when transitioning routes?

Is there any way to achieve a new SSR call to the backend for each route I navigate() with useNavigate() within a [slug] pattern? I have a createAsync() in [slug].tsx that is supposed to get some data from the backend based on the route I get to, but it triggers only with a full refresh of the tab in the browser or fisrt page load, if I try to navigate() from inside the app the createAsync() function is not called anymore, it's a "use server" declared function, with a cache() wrapper, just like the examples on docs. Ideally I wouldn't use <A> components and stick to useNavigate() but I'm open to anything would make this work. Maybe this is the correct behavior of my setup but I couldn't find anything related to my use-case in the docs
4 Replies
Madaxen86
Madaxen863w ago
Could share some code? How do you get the slug param etc.
mihaaai
mihaaai3w ago
Sure: /path/to/[slug].tsx This is the page itself
import { cache, useParams } from "@solidjs/router";
import { getApiEndpoint } from "~/lib/websocket";

export const getInitialProps = cache(async () => {
"use server";
const params = useParams();
// make call to backend with params.slug
const res = await apiService.fetch(url);
const info = await res.json();

if (res.status === 200) return info;
return {};
}, "slug");

// Adding this or not doesn't make difference
export const route = {
load: () => getInitialProps(),
};

const Slug = () => {
const info = createAsync(async () => await getInitialProps());
return <div>{info().user}</div>
}

export default Slug
import { cache, useParams } from "@solidjs/router";
import { getApiEndpoint } from "~/lib/websocket";

export const getInitialProps = cache(async () => {
"use server";
const params = useParams();
// make call to backend with params.slug
const res = await apiService.fetch(url);
const info = await res.json();

if (res.status === 200) return info;
return {};
}, "slug");

// Adding this or not doesn't make difference
export const route = {
load: () => getInitialProps(),
};

const Slug = () => {
const info = createAsync(async () => await getInitialProps());
return <div>{info().user}</div>
}

export default Slug
NavigatorItem.tsx This is one of the navigation components that is mounted right inside the <Router> but not inside the [slug].tsx page
import { useNavigate } from "@solidjs/router";

const NavigatorItem = () => {
const navigate = useNavigate();
return <div onClick={() => navigate("/path/to/another-slug", { replace: true })}>...</div>
}
import { useNavigate } from "@solidjs/router";

const NavigatorItem = () => {
const navigate = useNavigate();
return <div onClick={() => navigate("/path/to/another-slug", { replace: true })}>...</div>
}
app.tsx The Root:
import { Router } from "@solidjs/router";
import { FileRoutes } from "@solidjs/start/router";
import { MetaProvider, Title } from "@solidjs/meta";
import { Suspense } from "solid-js";

import NavigatorItem from "~/components/Header/NavigatorItem";
import "./app.css";

const App = () => {
return (
<Router
root={(props) => (
<MetaProvider>
<Title>App</Title>

<NavigatorItem />
<Suspense>{props.children}</Suspense>
</MetaProvider>
)}
>
<FileRoutes />
</Router>
);
};

export default App;
import { Router } from "@solidjs/router";
import { FileRoutes } from "@solidjs/start/router";
import { MetaProvider, Title } from "@solidjs/meta";
import { Suspense } from "solid-js";

import NavigatorItem from "~/components/Header/NavigatorItem";
import "./app.css";

const App = () => {
return (
<Router
root={(props) => (
<MetaProvider>
<Title>App</Title>

<NavigatorItem />
<Suspense>{props.children}</Suspense>
</MetaProvider>
)}
>
<FileRoutes />
</Router>
);
};

export default App;
The problem is that getInitialProps() gets triggered only on full refresh or first load, if I click on <NavigatorItem /> the route changes but the content isn't fetched for the new slug
Madaxen86
Madaxen863w ago
You shouldn't use useParams in a server function. Those hooks are meant to be used inside components. You may pass the the slug instead as a param to the server function like this:
const getInitialProps = cache(async (slug: string) => {
'use server';
return fetch(url + slug) //or whatever
}, 'get-nums');

export const route = {
preload: ({ params }) => getInitialProps(params.slug), //preload provides a callback with params,...
} satisfies RouteDefinition;

export default function Home() {
const params = useParams();
const info = createAsync(() => getInitialProps(params.slug));
//...
return "..."
}
const getInitialProps = cache(async (slug: string) => {
'use server';
return fetch(url + slug) //or whatever
}, 'get-nums');

export const route = {
preload: ({ params }) => getInitialProps(params.slug), //preload provides a callback with params,...
} satisfies RouteDefinition;

export default function Home() {
const params = useParams();
const info = createAsync(() => getInitialProps(params.slug));
//...
return "..."
}
And instead of using a div with onClick you may prefer to use an anchor tag a or A so hovering them will trigger the preload function to fetch the new data before the user click. This way you have almost immediate page transitions as the data is probably already there when the user clicks.
mihaaai
mihaaai3w ago
Thanks for the help, indeed using the slug as an argument to getInitialProps solved the problem, not everything works as expected! I've been reading about not using the use* functions on the server but damn, the docs are so much empty... Kudos! 🙏
Want results from more Discord servers?
Add your server