Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: add HydrateFallback to the data-loading doc #12601

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 52 additions & 1 deletion docs/start/framework/data-loading.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Data is provided to the route component from `loader` and `clientLoader`.

Loader data is automatically serialized from loaders and deserialized in components. In addition to primitive values like strings and numbers, loaders can return promises, maps, sets, dates and more.

The type for the `loaderData` prop is [automatically generated][type-safety].

## Client Data Loading

`clientLoader` is used to fetch data on the client. This is useful for pages or full projects that you'd prefer to fetch data from the browser only.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might introduce HydrateFallback here since it can/should be used for this scenario where you have only a clientLoader, and no loader. In that scenario, clientLoader.hydrate defaults to true because we have to run it on hydration if we have no loaderData from the server.

Then below you can mention:

  • When you provide a server loader in addition to a clientLoader, we can render the during SSR using the server loader data
  • If if we do not want to render the route with only server loaderData, then set clientLoader.hydrate=true to run on hydration and provide a HydrateFallback to render until clientLoader completes

Expand Down Expand Up @@ -124,10 +126,58 @@ export async function loader({ params }: Route.LoaderArgs) {
}

export async function clientLoader({
serverLoader,
params,
}: Route.ClientLoader) {
const serverData = await serverLoader();
const res = await fetch(`/api/products/${params.pid}`);
Comment on lines +132 to +133
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels redundant? In remix we showed off the common use cases in a dedicated page so maybe we pick what we think is the most common (presumably "skip the hop") and inline it into this doc but then we copy over the Remix doc and link off to it for advanced use cases?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah agreed. Initially I was combining that doc with the full stack data doc we had, but then realize we already had this doc

I think a dedicated advanced data loading explanation or how-to is a good idea. I’ll try to put something together, even if it’s just a starting point we can improve upon later

return { ...serverData, ...res.json() };
}

export default function Product({
loaderData,
}: Route.ComponentProps) {
const { name, description } = loaderData;

return (
<div>
<h1>{name}</h1>
<p>{description}</p>
</div>
);
}
```

You can also force the client loader to run during hydration and before the page renders by setting the `hydrate` property on the function. In this situation you will want to render a `HydrateFallback` component to show a fallback UI while the client loader runs.

```tsx filename=app/product.tsx
// route("products/:pid", "./product.tsx");
import type { Route } from "./+types/product";
import { fakeDb } from "../db";

export async function loader({ params }: Route.LoaderArgs) {
return fakeDb.getProduct(params.pid);
}

export async function clientLoader({
serverLoader,
params,
}: Route.ClientLoader) {
const serverData = await serverLoader();
const res = await fetch(`/api/products/${params.pid}`);
return res.json();
return { ...serverData, ...res.json() };
}

// Note: if there is no `loader`, this is implied
clientLoader.hydrate = true as const;

export function HydrateFallback() {
return (
<div>
<h1>Product Loading...</h1>
<p>Description loading...</p>
</div>
);
}

export default function Product({
Expand All @@ -154,3 +204,4 @@ See also:

[advanced_data_fetching]: ../tutorials/advanced-data-fetching
[data]: ../../api/react-router/data
[type-safety]: ../../explanation/type-safety