solid router's Data API

I'm a little bit confused about createAsync with the load function In the example of the GitHub repo, there's a cache fn to fetch data
const getUser = cache((id) => {
return (await fetch(`/api/users${id}`)).json()
}, "users")
const getUser = cache((id) => {
return (await fetch(`/api/users${id}`)).json()
}, "users")
Then we attached it to the load function of the Route
import { lazy } from "solid-js";
import { Route } from "@solidjs/router";
import { getUser } from ... // the cache function

const User = lazy(() => import("./pages/users/[id].js"));

// load function
function loadUser({params, location}) {
void getUser(params.id)
}

// Pass it in the route definition
<Route path="/users/:id" component={User} load={loadUser} />;
import { lazy } from "solid-js";
import { Route } from "@solidjs/router";
import { getUser } from ... // the cache function

const User = lazy(() => import("./pages/users/[id].js"));

// load function
function loadUser({params, location}) {
void getUser(params.id)
}

// Pass it in the route definition
<Route path="/users/:id" component={User} load={loadUser} />;
Then in the current route component, we have to again attach the fetch function to the create async
// pages/users/[id].js
import { getUser } from ... // the cache function

export default function User(props) {
const user = createAsync(() => getUser(props.params.id));
return <h1>{user().name}</h1>;
}
// pages/users/[id].js
import { getUser } from ... // the cache function

export default function User(props) {
const user = createAsync(() => getUser(props.params.id));
return <h1>{user().name}</h1>;
}
Then why can't we just attach to the create async only? Why are we attaching the function to both Route's load fn and create async? This kinda confused me
6 Replies
lxsmnsyc
lxsmnsyc6mo ago
The loadUser step is for preloading the data. createAsync is for actually reading the data. loadUser can run before the route renders, or if the user has the intent to visit it To be honest you can skip the load property if you wanted to
TaQuanMinhLong
TaQuanMinhLong6mo ago
Thanks, got it so createAsync will fetch data on the client side only? since it's a wrapper around create resource? what about fetching data from the server side?
lxsmnsyc
lxsmnsyc6mo ago
no, it runs on both ends, like createResource
what about fetching data from the server side?
Always createResource, that's how Solid achieves streaming SSR
TaQuanMinhLong
TaQuanMinhLong6mo ago
so we can just ignore the RouteLoadFunc? i tried implement like this
// root.tsx
import { Router, Route, cache, useParams, A } from "@solidjs/router";
import { createResource } from "solid-js";

export function Index() {
return (
<>
<div>Hello World</div>
</>
);
}

export function User() {
const params = useParams();
const [user] = createResource(() => fetchUser(params.id!));
return (
<div>
<A href="/">Home</A>
<code>{JSON.stringify(user())}</code>
</div>
);
}

const fetchUser = cache(async (id: string) => {
const res = await fetch(`http://localhost:5800/api/users/${id}`);
const data = await res.json();
if (!res.ok) throw data;
return data as { id: string; fullname: string; division: string };
}, "user");

export default function Root(props: { url?: string }) {
return (
<Router url={props.url}>
<Route path="/" component={Index} />
<Route path="/users/:id" component={User} />
</Router>
);
}
// root.tsx
import { Router, Route, cache, useParams, A } from "@solidjs/router";
import { createResource } from "solid-js";

export function Index() {
return (
<>
<div>Hello World</div>
</>
);
}

export function User() {
const params = useParams();
const [user] = createResource(() => fetchUser(params.id!));
return (
<div>
<A href="/">Home</A>
<code>{JSON.stringify(user())}</code>
</div>
);
}

const fetchUser = cache(async (id: string) => {
const res = await fetch(`http://localhost:5800/api/users/${id}`);
const data = await res.json();
if (!res.ok) throw data;
return data as { id: string; fullname: string; division: string };
}, "user");

export default function Root(props: { url?: string }) {
return (
<Router url={props.url}>
<Route path="/" component={Index} />
<Route path="/users/:id" component={User} />
</Router>
);
}
// entry-client.tsx
import "./index.css";
import { hydrate } from "solid-js/web";
import Root from "./root";

hydrate(() => <Root />, document.getElementById("root") as HTMLElement);
// entry-client.tsx
import "./index.css";
import { hydrate } from "solid-js/web";
import Root from "./root";

hydrate(() => <Root />, document.getElementById("root") as HTMLElement);
// entry-server.tsx
import { renderToString } from "solid-js/web";
import Root from "./root";

export function render(url?: string) {
const html = renderToString(() => <Root url={url} />);
return { html };
}
// entry-server.tsx
import { renderToString } from "solid-js/web";
import Root from "./root";

export function render(url?: string) {
const html = renderToString(() => <Root url={url} />);
return { html };
}
then run the build
"scripts": {
"build": "bun build:client && bun build:server",
"build:client": "vite build --ssrManifest --outDir dist/client",
"build:server": "vite build --ssr src/entry-server.tsx --outDir dist/server",
}
"scripts": {
"build": "bun build:client && bun build:server",
"build:client": "vite build --ssrManifest --outDir dist/client",
"build:server": "vite build --ssr src/entry-server.tsx --outDir dist/server",
}
then i tried running this script with bun
import { render } from "./frontend/dist/server/entry-server";
const html = render("/users/60507945");
console.log(html);
import { render } from "./frontend/dist/server/entry-server";
const html = render("/users/60507945");
console.log(html);
but the user data does not include in the html result retried with createAsync instead, the script did call the api, but the html return still does not have user data
lxsmnsyc
lxsmnsyc6mo ago
- You're using renderToString, it's not capable of server-side data fetching. You have to use renderToStringAsync or renderToStream. - Suspense is also a requirement for the SSR to know when to wait
TaQuanMinhLong
TaQuanMinhLong6mo ago
thanks, got it working