Skip to content

Commit

Permalink
use transition
Browse files Browse the repository at this point in the history
  • Loading branch information
robertotcestari committed Feb 25, 2025
1 parent c8cb435 commit c715e36
Show file tree
Hide file tree
Showing 8 changed files with 274 additions and 0 deletions.
6 changes: 6 additions & 0 deletions apps/next-intermediario/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ export default defineConfig({
directory: '04-estado-na-url',
},
},
{
label: 'useTransition',
autogenerate: {
directory: '04a-use-transition',
},
},
{
label: 'Rotas de API',
autogenerate: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
title: useTransition e startTransition
---

Agora que temos nosso estado na URL, uma coisa interessante que podemos fazer é um indicador de loading no campo de busca para um feedback visual de que os dados estão sendo buscados.

Um bom candidato para isso é usar o par `useTransition / startTransition`. Mas antes vamos entender rapidamente o que são eles.

## O hook `useTransition()`

O Hook `useTransition` nos retorna uma variável e uma função `startTransition`:

```typescript
const [isPending, startTransition] = useTransition()
```

o `isPending` é uma variável que indica se uma transição está em andamento. Você pode usar para mostrar um indicador de *loading*.

A função `startTransition` serve para "envelopar" uma função que faz alguma alteração no estado da aplicação. Quando fazemos isso estamos dizendo que aquela alteração é uma transição e não uma atualização imediata - ou seja, ela não é urgente.

Isso é importante porque faz com que essa atualização de estado não bloqueie a interface do usuário.

## Três casos de uso

1. Evitar que uma atualização de estado bloqueie a interface do usuário.
2. Atualizações otimistas (*optimistic ui*).
3. Indicação de loading - por exemplo em um input de busca.

## Onde não faz sentido usar?

- Não faz sentido usar `startTransition` em *server components*.
- Não faz sentido usar `startTransition` quando você não está lidando com uma atualização de estado.
- Não faz sentido usar `startTransition` quando as atualizações são criticas e devem ser feitas imediatamente.

A regra básica é que `startTransition` serve para atualizações não urgentes e que podem ser adiadas ou interrompidas.
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
---
title: useTransition na prática
---

Entendemos razoavelmente o papel de `useTransition` e `startTransition`. Agora está na hora de usar isso na prática no nosso projeto.

## Exercício - Loading no input de busca

Vamos adicionar um indicador de loading no nosso input de busca. Quando a busca está em andamento vamos mostrar um *spinner* ao invés do ícone de lupa.

O carregamento deve durar enquanto buscamos os dados. E deve ser interrompido

:::tip
use a animação spin do tailwind para mostrar o loading.
:::

### Solução

- Primeiro, vamos chamar o hook `useTransition` no componente `SearchInput`.
- Depois vamos desconstruir o `startTransition` e o `isPending` em variáveis.
- Vamos usar o `isPending` para mostrar o *spinner* quando a busca está em andamento.
- Vamos usar o `startTransition` para envelopar a função que busca os dados.

#### Código

```tsx ins={26,28,6,12,40-44}
// src/app/vagas/_components/search-bar.tsx
"use client";
import { Input } from "@/components/ui/input";
import { Loader2, Search } from "lucide-react";
import { useRouter, useSearchParams } from "next/navigation";
import { useTransition } from "react";
import { useDebouncedCallback } from "use-debounce";

export default function SearchBar() {
const searchParams = useSearchParams();
const previousSearchText = searchParams?.get("search") || "";
const [isPending, startTransition] = useTransition();
const router = useRouter();

const handleChange = useDebouncedCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
const urlSearchParams = new URLSearchParams(searchParams ?? undefined);
const searchText = event.target.value;

if (searchText) {
urlSearchParams.set("search", searchText);
} else {
urlSearchParams.delete("search");
}

startTransition(() => {
router.replace("?" + urlSearchParams.toString());
});
},
500,
);

return (
<div className="mb-8">
<Input
type="search"
placeholder="Busque uma vaga..."
className="py-6 text-lg"
icon={
isPending ? (
<Loader2 className="size-4 animate-spin" />
) : (
<Search className="size-4" />
)
}
onChange={handleChange}
defaultValue={previousSearchText}
/>
</div>
);
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
title: Rotas API no Next.js
---

## Rotas API no Next.js

O Next.js foi inovador quando foi lançado justamente por lançar mão dos benefícios de uma aplicação que rodava em um servidor.

Ao contrário do tradicional SPA React, uma aplicação Next.js está rodando em um servidor e possui acesso, portanto, ao ambiente *node* do servidor.

Um dos benefícios foi poder transformar o *framework frontend* em um *framework fullstack*. E muito disso foi possível porque o Next.js implementou as chamadas **Rotas API**.

Com o Next.js v13+, as Rotas APIs foram renomeadas para `Route Handlers`.

## Anatomia das Route Handlers

### O Arquivo

Uma rota API é definida da mesma forma que uma página `page`. Exceto que o nome agora deverá ser `route.ts` ou `route.js`.

:::caution
Perceba que as rotas API são processadas inteiramente no servidor - dessa forma não aceitam `JSX`, e portanto a extensão não pode ser `jsx|tsx`.
:::

### A função

Sabemos que elas são definidas dentro de um `route.ts`. Certo, mas o que é definido nesse arquivo?

Necessariamente em uma rota API você deverá exportar uma (ou mais de uma) das seguintes funções assíncronas:

- GET
- HEAD
- POST
- PUT
- DELETE
- PATCH
- OPTIONS

Como não é difícil perceber, cada uma dessas funções exportadas se referirá a um método HTTP.

Exemplo abaixo:

```typescript
export async function GET() {}

export async function HEAD() {}

export async function POST() {}

export async function PUT() {}

export async function DELETE() {}

export async function PATCH() {}

```

### Os parâmetros

Cada uma dessas funções exportadas receberá dois parâmetros opcionais:

- `request`: um objeto do tipo `Request`
- `context`: um objeto do tipo `NextRequest`

#### O parâmetro `request`

O parâmetro `request` é um objeto do tipo `Request` que representa a requisição HTTP. O mais legal é que é um Web API Request.

Isso significa que você pode usar todos os métodos disponíveis no `Request` do browser.

Por exemplo, você pode acessar os parâmetros da requisição através do `searchParams`:

```typescript
export async function GET(request: Request) {
const { searchParams } = new URL(request.url)
const name = searchParams.get('name')

return new Response(`Hello ${name}`)
}
```

#### O parâmetro `context`

O parâmetro `context` é um objeto que possui uma prop `params`, que é uma promise que resolve em um objeto que contém os parâmetros da rota.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
title: Ainda faz sentido usar rotas API no Next.js?
---

Sendo direto ao ponto: depois que surgiram as Server Actions / Server Functions, *não faz mas tanto sentido utilizar rotas de API*. Isso porque elas eram especialmente úteis para acessar dados do servidor diretamente de componentes frontend. Agora que componentes de cliente podem acessar *server actions* e *server functions*, não há mais necessidade de se utilizar rotas de API para isso.

Aliás, *é muito mais fácil* usar uma `server action` do que uma rota de API para isso.

Claro que ainda existem alguns casos de uso:

1. Quando você precisa de uma rota que não é uma rota de página. Esse caso de uso pode ser útil quando você precisa acessar apenas os dados do servidor.
2. Aplicações mobile, onde você precisa fazer requisições HTTP para o backend.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
title: Server Actions ou Server Functions?
---

## Server Actions ou Server Functions?

Até Setembro de 2024, não havia distinção entre **Server Actions** e **Server Functions**. Ambos eram chamados de **Server Actions**.

A partir dessa data criou-se a distinção entre os dois.

![alt text](image.png)

## Server Functions

### Definição de Server Function

Uma **server function** é:

- uma função assíncrona
- definida pela diretiva `use server`
- definida no lado do servidor
- inline em um server component com a diretiva `use server` no início da função
- em um arquivo separado com a diretiva `use server` no topo do arquivo
- que pode ser utilizada tanto em *client components* quanto em *server components*

### Chamando uma Server Function

Uma server function pode ser chamada no cliente ou no servidor. Mas não faz muito sentido chamá-la no servidor, já que no servidor você pode usar a lógica diretamente no componente sem precisar de uma função separada.

#### Chamando no Cliente

Você pode invocar uma Server Function:

- em um formulário (ela receberá o objeto `formData` como argumento)
- em um event handler.

## Server Action

### Definição de Server Action

Uma **server action** é:

- uma **Server Function**
- que é chamada diretamente em um `form` ou;
- chamada dentro de uma `action` de um `form`

Quando a server action é chamada em um `form`, ela recebe o objeto `formData` como primeiro argumento.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
title: Server Functions - Outros tópicos
---

## Invocando Server Functions dentro e fora de um formulário

Quando uma Server Function é chamada em um formulário algumas coisas "mágicas" acontecem:

1. Um objeto `formData` é passado como primeiro argumento (assinatura muda)
2. A função é automaticamente envelopada em um

### Passando argumentos para uma Server Function
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit c715e36

Please sign in to comment.