Skip to content

Commit

Permalink
Update LoaderArgs/ActionArgs in docs (#7426)
Browse files Browse the repository at this point in the history
  • Loading branch information
brophdawg11 authored Sep 15, 2023
1 parent 1fef5a1 commit be1a68b
Show file tree
Hide file tree
Showing 35 changed files with 439 additions and 240 deletions.
4 changes: 3 additions & 1 deletion docs/discussion/concurrency.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ In UI components like comboboxes, each keystroke can trigger a network request.
```tsx filename=app/routes/city-search.tsx
import { json } from "@remix-run/react";

export async function loader({ request }: LoaderArgs) {
export async function loader({
request,
}: LoaderFunctionArgs) {
const { searchParams } = new URL(request.url);
const cities = await searchCities(searchParams.get("q"));
return json(cities);
Expand Down
4 changes: 3 additions & 1 deletion docs/discussion/form-vs-fetcher.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,9 @@ function useMarkAsRead({ articleId, userId }) {
Anytime you show the user avatar, you could put a hover effect that fetches data from a loader and displays it in a popup.

```tsx filename=app/routes/user.$id.details.tsx
export async function loader({ params }: LoaderArgs) {
export async function loader({
params,
}: LoaderFunctionArgs) {
return json(
await fakeDb.user.find({ where: { id: params.id } })
);
Expand Down
4 changes: 3 additions & 1 deletion docs/discussion/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,9 @@ export default function Projects() {
// Actions only run on the server and handle POST
// PUT, PATCH, and DELETE. They can also provide data
// to the component
export async function action({ request }: ActionArgs) {
export async function action({
request,
}: ActionFunctionArgs) {
const form = await request.formData();
const errors = validate(form);
if (errors) {
Expand Down
4 changes: 2 additions & 2 deletions docs/file-conventions/routes.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ app/
Remix will parse the value from the URL and pass it to various APIs. We call these values "URL Parameters". The most useful places to access the URL params are in [loaders][loader] and [actions][action].

```tsx
export function loader({ params }: LoaderArgs) {
export function loader({ params }: LoaderFunctionArgs) {
return fakeDb.getAllConcertsForCity(params.city);
}
```
Expand All @@ -127,7 +127,7 @@ You'll note the property name on the `params` object maps directly to the name o
Routes can have multiple dynamic segments, like `concerts.$city.$date`, both are accessed on the params object by name:

```tsx
export function loader({ params }: LoaderArgs) {
export function loader({ params }: LoaderFunctionArgs) {
return fake.db.getConcerts({
date: params.date,
city: params.city,
Expand Down
8 changes: 6 additions & 2 deletions docs/guides/api-routes.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ You can `useFetcher` for cases like this. And once again, since Remix in the bro
For example, you could have a route to handle the search:

```tsx filename=app/routes/city-search.tsx
export async function loader({ request }: LoaderArgs) {
export async function loader({
request,
}: LoaderFunctionArgs) {
const url = new URL(request.url);
return json(
await searchCities(url.searchParams.get("q"))
Expand Down Expand Up @@ -93,7 +95,9 @@ function CitySearchCombobox() {
In other cases, you may need routes that are part of your application, but aren't part of your application's UI. Maybe you want a loader that renders a report as a PDF:

```tsx
export async function loader({ params }: LoaderArgs) {
export async function loader({
params,
}: LoaderFunctionArgs) {
const report = await getReport(params.id);
const pdf = await generateReportPDF(report);
return new Response(pdf, {
Expand Down
8 changes: 5 additions & 3 deletions docs/guides/bff.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ Mature apps already have a lot of backend application code in Ruby, Elixir, PHP,

Because Remix polyfills the Web Fetch API, you can use `fetch` right from your loaders and actions to your backend.

```tsx lines=[9,15,19]
import type { LoaderArgs } from "@remix-run/node"; // or cloudflare/deno
```tsx lines=[11,17,21]
import type { LoaderFunctionArgs } from "@remix-run/node"; // or cloudflare/deno
import { json } from "@remix-run/node"; // or cloudflare/deno
import escapeHtml from "escape-html";

export async function loader({ request }: LoaderArgs) {
export async function loader({
request,
}: LoaderFunctionArgs) {
const apiUrl = "http://api.example.com/some-data.json";
const res = await fetch(apiUrl, {
headers: {
Expand Down
12 changes: 9 additions & 3 deletions docs/guides/constraints.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,9 @@ import { json } from "@remix-run/node"; // or cloudflare/deno

import { removeTrailingSlash } from "~/http";

export const loader = async ({ request }: LoaderArgs) => {
export const loader = async ({
request,
}: LoaderFunctionArgs) => {
removeTrailingSlash(request.url);
return json({ some: "data" });
};
Expand All @@ -218,7 +220,9 @@ It reads much nicer as well when you've got a lot of these:

```tsx
// this
export const loader = async ({ request }: LoaderArgs) => {
export const loader = async ({
request,
}: LoaderFunctionArgs) => {
return removeTrailingSlash(request.url, () => {
return withSession(request, (session) => {
return requireUser(session, (user) => {
Expand All @@ -231,7 +235,9 @@ export const loader = async ({ request }: LoaderArgs) => {

```tsx
// vs. this
export const loader = async ({ request }: LoaderArgs) => {
export const loader = async ({
request,
}: LoaderFunctionArgs) => {
removeTrailingSlash(request.url);
const session = await getSession(request);
const user = await requireUser(session);
Expand Down
56 changes: 35 additions & 21 deletions docs/guides/data-loading.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,11 @@ If your server-side modules end up in client bundles, move the imports for those
When you name a file with `$` like `app/routes/users.$userId.tsx` and `app/routes/users.$userId.projects.$projectId.tsx` the dynamic segments (the ones starting with `$`) will be parsed from the URL and passed to your loader on a `params` object.

```tsx filename=app/routes/users.$userId.projects.$projectId.tsx
import type { LoaderArgs } from "@remix-run/node"; // or cloudflare/deno
import type { LoaderFunctionArgs } from "@remix-run/node"; // or cloudflare/deno

export const loader = async ({ params }: LoaderArgs) => {
export const loader = async ({
params,
}: LoaderFunctionArgs) => {
console.log(params.userId);
console.log(params.projectId);
};
Expand All @@ -71,11 +73,13 @@ Given the following URLs, the params would be parsed as follows:

These params are most useful for looking up data:

```tsx filename=app/routes/users.$userId.projects.$projectId.tsx lines=[8,9]
import type { LoaderArgs } from "@remix-run/node"; // or cloudflare/deno
```tsx filename=app/routes/users.$userId.projects.$projectId.tsx lines=[10-11]
import type { LoaderFunctionArgs } from "@remix-run/node"; // or cloudflare/deno
import { json } from "@remix-run/node"; // or cloudflare/deno

export const loader = async ({ params }: LoaderArgs) => {
export const loader = async ({
params,
}: LoaderFunctionArgs) => {
return json(
await fakeDb.project.findMany({
where: {
Expand All @@ -91,11 +95,13 @@ export const loader = async ({ params }: LoaderArgs) => {

Because these params come from the URL and not your source code, you can't know for sure if they will be defined. That's why the types on the param's keys are `string | undefined`. It's good practice to validate before using them, especially in TypeScript to get type safety. Using `invariant` makes it easy.

```tsx filename=app/routes/users.$userId.projects.$projectId.tsx lines=[2,5-6]
import type { LoaderArgs } from "@remix-run/node"; // or cloudflare/deno
```tsx filename=app/routes/users.$userId.projects.$projectId.tsx lines=[2,7-8]
import type { LoaderFunctionArgs } from "@remix-run/node"; // or cloudflare/deno
import invariant from "tiny-invariant";

export const loader = async ({ params }: LoaderArgs) => {
export const loader = async ({
params,
}: LoaderFunctionArgs) => {
invariant(params.userId, "Expected params.userId");
invariant(params.projectId, "Expected params.projectId");

Expand Down Expand Up @@ -147,13 +153,15 @@ export { db };
And then your routes can import it and make queries against it:

```tsx filename=app/routes/products.$categoryId.tsx
import type { LoaderArgs } from "@remix-run/node"; // or cloudflare/deno
import type { LoaderFunctionArgs } from "@remix-run/node"; // or cloudflare/deno
import { json } from "@remix-run/node"; // or cloudflare/deno
import { useLoaderData } from "@remix-run/react";

import { db } from "~/db.server";

export const loader = async ({ params }: LoaderArgs) => {
export const loader = async ({
params,
}: LoaderFunctionArgs) => {
return json(
await db.product.findMany({
where: {
Expand All @@ -177,7 +185,7 @@ export default function ProductCategory() {
If you are using TypeScript, you can use type inference to use Prisma Client generated types when calling `useLoaderData`. This allows better type safety and intellisense when writing code that uses the loaded data.

```tsx filename=app/routes/products.$productId.tsx
import type { LoaderArgs } from "@remix-run/node"; // or cloudflare/deno
import type { LoaderFunctionArgs } from "@remix-run/node"; // or cloudflare/deno
import { json } from "@remix-run/node"; // or cloudflare/deno
import { useLoaderData } from "@remix-run/react";

Expand All @@ -198,7 +206,9 @@ async function getLoaderData(productId: string) {
return product;
}

export const loader = async ({ params }: LoaderArgs) => {
export const loader = async ({
params,
}: LoaderFunctionArgs) => {
return json(await getLoaderData(params.productId));
};

Expand Down Expand Up @@ -228,14 +238,14 @@ For the Cloudflare Workers environment you'll need to [do some other configurati
This enables you to use the `PRODUCTS_KV` in a loader context (KV stores are added to loader context automatically by the Cloudflare Pages adapter):

```tsx
import type { LoaderArgs } from "@remix-run/cloudflare";
import type { LoaderFunctionArgs } from "@remix-run/cloudflare";
import { json } from "@remix-run/cloudflare";
import { useLoaderData } from "@remix-run/react";

export const loader = async ({
context,
params,
}: LoaderArgs) => {
}: LoaderFunctionArgs) => {
return json(
await context.PRODUCTS_KV.get(
`product-${params.productId}`,
Expand Down Expand Up @@ -263,7 +273,7 @@ While loading data it's common for a record to be "not found". As soon as you kn
export const loader = async ({
params,
request,
}: LoaderArgs) => {
}: LoaderFunctionArgs) => {
const product = await db.product.findOne({
where: { id: params.productId },
});
Expand All @@ -287,11 +297,13 @@ export const loader = async ({

URL Search Params are the portion of the URL after a `?`. Other names for this are "query string", "search string", or "location search". You can access the values by creating a URL out of the `request.url`:

```tsx filename=app/routes/products.tsx lines=[5-6]
import type { LoaderArgs } from "@remix-run/node"; // or cloudflare/deno
```tsx filename=app/routes/products.tsx lines=[7-8]
import type { LoaderFunctionArgs } from "@remix-run/node"; // or cloudflare/deno
import { json } from "@remix-run/node"; // or cloudflare/deno

export const loader = async ({ request }: LoaderArgs) => {
export const loader = async ({
request,
}: LoaderFunctionArgs) => {
const url = new URL(request.url);
const term = url.searchParams.get("term");
return json(await fakeProductSearch(term));
Expand Down Expand Up @@ -366,11 +378,13 @@ Then the url will be: `/products/shoes?brand=nike&brand=adidas`

Note that `brand` is repeated in the URL search string since both checkboxes were named `"brand"`. In your loader you can get access to all of those values with [`searchParams.getAll`][search-params-getall]

```tsx lines=[6]
import type { LoaderArgs } from "@remix-run/node"; // or cloudflare/deno
```tsx lines=[8]
import type { LoaderFunctionArgs } from "@remix-run/node"; // or cloudflare/deno
import { json } from "@remix-run/node"; // or cloudflare/deno

export async function loader({ request }: LoaderArgs) {
export async function loader({
request,
}: LoaderFunctionArgs) {
const url = new URL(request.url);
const brands = url.searchParams.getAll("brand");
return json(await getProducts({ brands }));
Expand Down
26 changes: 17 additions & 9 deletions docs/guides/data-writes.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,9 @@ When the user submits this form, the browser will serialize the fields into a re
The data is made available to the server's request handler, so you can create the record. After that, you return a response. In this case, you'd probably redirect to the newly-created project. A remix action would look something like this:

```tsx filename=app/routes/projects.tsx
export async function action({ request }: ActionArgs) {
export async function action({
request,
}: ActionFunctionArgs) {
const body = await request.formData();
const project = await createProject(body);
return redirect(`/projects/${project.id}`);
Expand Down Expand Up @@ -171,12 +173,14 @@ export default function NewProject() {

Now add the route action. Any form submissions that are "post" will call your data "action". Any "get" submissions (`<Form method="get">`) will be handled by your "loader".

```tsx lines=[1,5-9]
import type { ActionArgs } from "@remix-run/node"; // or cloudflare/deno
```tsx lines=[1,5-11]
import type { ActionFunctionArgs } from "@remix-run/node"; // or cloudflare/deno
import { redirect } from "@remix-run/node"; // or cloudflare/deno

// Note the "action" export name, this will handle our form POST
export const action = async ({ request }: ActionArgs) => {
export const action = async ({
request,
}: ActionFunctionArgs) => {
const formData = await request.formData();
const project = await createProject(formData);
return redirect(`/projects/${project.id}`);
Expand Down Expand Up @@ -205,10 +209,12 @@ const [errors, project] = await createProject(formData);

If there are validation errors, we want to go back to the form and display them.

```tsx lines=[1,5,7-10]
```tsx lines=[1,7,9-12]
import { json, redirect } from "@remix-run/node"; // or cloudflare/deno

export const action = async ({ request }: ActionArgs) => {
export const action = async ({
request,
}: ActionFunctionArgs) => {
const formData = await request.formData();
const [errors, project] = await createProject(formData);

Expand All @@ -223,12 +229,14 @@ export const action = async ({ request }: ActionArgs) => {

Just like `useLoaderData` returns the values from the `loader`, `useActionData` will return the data from the action. It will only be there if the navigation was a form submission, so you always have to check if you've got it or not.

```tsx lines=[3,10,20,25-29,37,42-46]
import type { ActionArgs } from "@remix-run/node"; // or cloudflare/deno
```tsx lines=[3,12,22,27-31,39,44-48]
import type { ActionFunctionArgs } from "@remix-run/node"; // or cloudflare/deno
import { json, redirect } from "@remix-run/node"; // or cloudflare/deno
import { useActionData } from "@remix-run/react";

export const action = async ({ request }: ActionArgs) => {
export const action = async ({
request,
}: ActionFunctionArgs) => {
// ...
};

Expand Down
4 changes: 3 additions & 1 deletion docs/guides/envvars.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ export const onRequest = createPagesFunctionHandler({
And they'll be available via Remix's `context` in your `loader`/`action` functions:

```tsx
export const loader = async ({ context }: LoaderArgs) => {
export const loader = async ({
context,
}: LoaderFunctionArgs) => {
console.log(context.env.SOME_SECRET);
};
```
Expand Down
Loading

0 comments on commit be1a68b

Please sign in to comment.