Skip to content

Commit

Permalink
update steps
Browse files Browse the repository at this point in the history
  • Loading branch information
aurorascharff committed Nov 27, 2024
1 parent 85e66bb commit cb16cfd
Showing 1 changed file with 40 additions and 26 deletions.
66 changes: 40 additions & 26 deletions STEPS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,29 @@ Go to split view.

We recognize the boilerplate from the slides. We have a `vite.config.ts`, react-router.config.js` file, a `tsconfig.json` file. Prisma stuff and public folder. Eslint and prettier are also set up.

The .react-router folder here is autogenerated to provide type safety to our routes which is a very new feature that is currently being improved.

An `app` folder, and i revealed the entry.server and entry.client files. Here are components and the routes.

## Routing: /routes, routes.ts

Let's start with the routes folder. I'm using file-based routing here and my RRM's are defined as files, and we have to follow a certain convention. THis is called flat routes, we define routes with dot and other chars. Maybe you like it, maybe you don't. Common in most meta-frameworks, but with very different rules. There is also a code-option as mentioned. Show routes.ts file to define code based routing. Route modules are code split so we can lazily load js as we navigate to new routes for better performance.

## Routes: root.tsx
The .react-router folder here is autogenerated to provide type safety to our routes which is a very new feature that is currently being improved.

## Remix Route Modules: root.tsx

Start with root.tsx. This is the root route module. It has a special Layout export' returning the root document with the metadata and links that are collected from link functions and meta functions for route modules, and inserted into the head. It's then returning the children of the layout, which is the default export, App.

## Layouts: root.tsx

App has a Outlet component, the outlet is for defining this routes children routes, using nested layouts here. This allows us to build complex UIs. And remix can fetch data for all nested routes in parallel.

Start with root.tsx. This is the root layout. It's returning the root document with the metadata and links that are collected from link functions and meta functions for route modules, and inserted into the head. It's then returning the children of the layout, which is the default export, App. It's using nested layouts to render the children.
## Error boundaries: root.tsx

Don't show error boundary.
Error boundaries for each route module using useRouteError. Here we catch any error that might occur. We can show a custom error message here. We can also use the error hook to get the error and show it in the UI.

## UI: index.tsx

Lets look at Index.tsx. Showcase in app the page. Use the web! Central part of Remix. Remix feels familiar to old school devs. Less react-y. Learning the web when learning Remix. This is a Remix Route Module for the index page. It's using the Remix Link component to provide client side navigation.
Lets look at index.tsx. Default/index child of the root.tsx. Showcase in app the page. Use the web! Central part of Remix. Remix feels familiar to old school devs. Less react-y. Learning the web when learning Remix. This is a Remix Route Module for the index page. It's using the Remix Link component to provide client side navigation.

It's also defining metadata to be collected for the head with this meta function. Can see it in the title "Remix Jokes".

Expand All @@ -42,44 +48,32 @@ Index.tsx. Here I'm using tailwind, but Remix also has this feature called links

## Data fetching: jokes.tsx

Lets move to the rest of the files. Jokes.tsx. Go to jokes in demo. Another route module for the url /jokes. This route module has a loader! Fetching data from the database - a list of jokes. It's using Prisma to interact with the database. It's using the loader to fetch the data and return it to the component using the loaderData prop and type safe Route.ComponentProps. Importing autogenerated types from .react router to get type safe loader. We don't need useEffect fetching, kind of like getServerSideProps in Next.js.
Lets move to the rest of the files. Jokes.tsx is a relative route. Go to jokes in demo. Another route module for the url /jokes. This route module has a loader! Fetching data from the database - a list of jokes. It's using Prisma to interact with the database. It's using the loader to fetch the data and return it to the component using the loaderData prop and type safe Route.ComponentProps. Importing autogenerated types from .react router to get type safe loader. We don't need useEffect fetching, kind of like getServerSideProps in Next.js.

It's defining lots of UI from this default component. A header, a sidebar with jokes and Links, and mapping jokes to link with prefetch. If prefetched, we don't see this indicator. Show prefetch in console second screen.

We will start to see these unfamiliar, Remix specific hooks around here which is the browser framework. This one is called useNavigation. It will give you the current state of the navigation, and provide i.e whether the router is navigating, which we use here to mark the page as pending with css. This hook adds on top of our base case HTML web standard document with additional client-side, js enhanced features. This is Remix's way to do progressive enhancement.

## Layouts: jokes.tsx
It's defining lots of UI from this default component. A header, a sidebar with jokes and Links, and mapping jokes to link with prefetch. If prefetched, we don't see this indicator.

Further down there is a Outlet component and a footer, the outlet is for defining this routes children routes, using nested layouts here. This allows us to build complex UIs. And remix can fetch data for all nested routes in parallel.

## Layouts: jokes._index.tsx
## Errors: jokes._index.tsx

The index route is the default child of the jokes route, and is rendered in the outlet on the route. It's using a loader to fetch a random joke. Throws a 404 if no joke is found.

## Errors: jokes._index.tsx

Error boundaries for each route module using useRouteError. Here we catch the error that might occur when posting a new joke to the db. We can show a custom error message here. We can also use the error hook to get the error and show it in the UI. Here catching a 404 error to display a custom message with a link to the jokes page.
Here we catch the error that might occur when posting a new joke to the db. We can show a custom error message here. We can also use the error hook to get the error and show it in the UI. Here catching a 404 error to display a custom message with a link to the jokes page.

## Mutations: jokes.new.tsx

Relative route. Lets move to the jokes.new.tsx route module. Default export has a form. Don't worry about this optimistic update yet let's just look at the form. This is similar to our example from the slides. Remix Form component will provide extra features on top of default form component, such as client side nav avoiding full page reload.
Relative route. Lets move to the jokes.new.tsx route module. Default export has a form, capital letter. This is similar to our example from the slides. Remix Form component will provide extra features on top of default form component, such as client side nav avoiding full page reload.

There is a method post on this, which will call the route for the route module with its action. The action validates using zod and returns errors if invalid inside a badRequest wrapper. Customizable. Then it redirects. Show console of network tab to show the post and get requests. GET data joke.new. POST data joke.new with location in the equest. New GET for revalidation.

We don't need api routes to talk to our server from the client.
We don't need api routes to talk to our server from the client. Route module is it's own API route talking to itself.

## Revalidation: jokes.new.tsx

Data is automatically revalidates. No fuss with adding items to lists. It's always the same. No manual revalidation. It's always in sync with the server. We can push or filter lists client side.

Show network tab loading and revalidating.

## Optimistic UI: jokes.new.tsx

We also now create this additional UI enhancement with a isSubmitting value, that checks whether the navigation is not idle and is on the action "jokes/new". It will disable the button when the form submits.

In addition it's doing optimistic UI by returning a view of a JokeDisplay if the validation succeeds. This is a client side update that will show the joke in the list before the server has responded.

## RRM for single joke: joke.$jokeid.tsx

Lets move to a more complex route module with lots of features.
Expand All @@ -90,9 +84,29 @@ It's also using the useRouteError hook to catch any errors that might occur when

JokeDisplay can delete a joke, but this is now a relative URL using a action="/destroy". Using a form as well which is unfamiliar for React devs who usually just do button onClicks. This is a relative URL that will call the route module with the action destroy. Defined with file based routing. It's using the action to delete the joke from the database, and redirect. Web standard stuff that at least for me is very unfamiliar as a React SPA dev originally. Delete checks for the intent to be expected. This can be used to handle multiple actions in the same route module. Then deletes from the db and redirects.

Enhanced with a disabled button again when the navigation is not idle and is on the intent "delete".
It can also be favorited. Maybe a bit weird to see a form for this. This triggers the relative URL action="/". This is a relative URL that will call the route module with the action favorite. Here we use a fetcher Form because we are not navigating anywhere and we don't want to trigger a new push to the router.

## Browser framework

The app has some, realistic added delay and that makes it not feel so good. And until we didn't actually use that much of the browser framework, only Form and Links and prefetching etc. Does this feel like React? We are able to create all this without any React-stuff. Amazing right?

Let's switch to another version of the app that has some UI enhancements using more of the browser framework of Remix.

### UI enhancements: jokes.tsx

In the jokes.tsx, we will use a Remix specific hooks around here which is the browser framework. This one is called useNavigation. It will give you the current state of the navigation, and provide i.e whether the router is navigating, which we use here to mark the page as pending with css. This hook adds on top of our base case HTML web standard document with additional client-side, js enhanced features. This is Remix's way to do progressive enhancement.

### Optimistic UI: jokes.new.tsx

We have also created this additional UI enhancement with a isSubmitting value, that checks whether the navigation is not idle and is on the action "jokes/new". It will disable the button when the form submits.

In addition it's doing optimistic UI by returning a view of a JokeDisplay if the validation succeeds. This is a client side update that will show the joke in the list before the server has responded.

### UI enhancements: joke.$jokeid.tsx

Delete is enhanced with a disabled button again when the navigation is not idle and is on the intent "delete".

It can also be favorited. Maybe a bit weird to see a form for this. This triggers the relative URL action="/". This is a relative URL that will call the route module with the action favorite. Here we use a fetcher Form because we are not navigating anywhere and we don't want to trigger a new push to the router. We can get the state of this fetcher form locally and do another optimistic update.
For the favorite, we can get the state of this fetcher form locally and do another optimistic update. Fetcher is local scoped to the component.

## Progressive enhancement turn off JS

Expand Down

0 comments on commit cb16cfd

Please sign in to comment.