Isso é MUITO melhor que o NextJS (TanStack Server Components)

BBetter Stack
Computing/SoftwareInternet Technology

Transcript

00:00:00React Server Components. Ame-os ou odeie-os. Parece que o ódio predomina hoje em dia, mas isso
00:00:04pode estar prestes a mudar, já que o TanStack entrou no jogo. É isso mesmo, agora temos TanStack
00:00:08Server Components, e eles adotaram uma abordagem bem diferente da do Next.js. Vamos dar uma olhada.
00:00:13Vou começar com um parágrafo do post de anúncio deles que acho que deixará
00:00:21muita gente tranquila. Diz o seguinte: "A maioria das pessoas pensa nos React Server Components de uma forma
00:00:26"server-first". O servidor é dono da árvore, useClient marca as partes interativas, e as convenções do framework
00:00:31decidem como tudo se encaixa. Isso transforma os React Server Components de uma primitiva útil
00:00:35em algo em torno do qual todo o seu aplicativo deve orbitar. Não achamos que você deva ter que aderir
00:00:40a todo esse modelo logo de início apenas para obter valor dos React Server Components."
00:00:45Essencialmente, o que eles estão dizendo é que não querem seguir o caminho do Next.js, onde são
00:00:48Server Components por padrão e você precisa da diretiva useClient onde quer ter
00:00:52interatividade. Em vez disso, o TanStack quer pensar nisso como: e se você pudesse usar React Server Components
00:00:57com a mesma granularidade com que busca JSON no cliente? Com esse objetivo em mente, vamos dar uma olhada em
00:01:01como eles realmente implementaram os Server Components, porque, spoiler, eu realmente gosto da maneira como
00:01:06eles fizeram. O que tenho aqui é um aplicativo normal do TanStack Start, então tudo no momento vai
00:01:10ser um componente de cliente, e a única coisa que fiz foram alguns pequenos passos de instalação necessários para
00:01:15colocar os Server Components para rodar, que basicamente é apenas instalar alguns pacotes e modificar seu
00:01:18vite.config. É assim que a página se parece atualmente; temos nosso componente "greeting" aqui, que
00:01:22deveria ser, atualmente, um componente de cliente, e no código ele é literalmente apenas um único componente React.
00:01:27Então, aqui embaixo, temos uma rota normal do TanStack, e estamos usando o componente "greeting" aqui. Agora, digamos
00:01:32que em nosso componente "greeting", você queira fazer alguma lógica no servidor. No meu caso, quero obter
00:01:36o hostname do sistema operacional e também algumas variáveis de ambiente que só estão disponíveis no servidor
00:01:40apenas para mostrar que isso está realmente rodando lá. No momento, se eu tentar usar os.hostname,
00:01:45não vai funcionar, já que esta é uma função do Node e não está disponível no navegador.
00:01:49O que precisamos fazer, então, é pegar nosso componente "greeting" e renderizá-lo no servidor, e o
00:01:53primeiro passo para fazer isso no TanStack Start é com uma simples Server Function. Como você pode ver, tenho
00:01:58uma aqui chamada "get greeting", e tudo o que estamos fazendo aqui dentro é usar a nova função "renderServer
00:02:01Component", colocando nosso componente dentro dela e retornando o Server Component renderizável
00:02:06que recebemos de volta. Você pode pensar nisso como algo tão simples quanto criar uma requisição GET para o nosso componente.
00:02:10Depois, tudo o que precisamos fazer é simplesmente buscar o componente da Server Function que criamos
00:02:14e podemos fazer isso dentro de um loader em uma rota aqui, então estamos apenas aguardando "get greeting" e então
00:02:18retornando isso também, e ele ainda é um Server Component renderizável. Então podemos usá-lo dentro de
00:02:23nossa rota aqui, usando "useLoaderData", e usamos o componente aqui embaixo, assim. Com isso,
00:02:27agora temos nosso primeiro TanStack Server Component. Você pode ver que os.hostname está funcionando agora e também
00:02:32puxando variáveis de ambiente que só estão disponíveis no servidor. Agora, a coisa que eu
00:02:36amo absolutamente nesta implementação é que, se você notar, a única coisa nova aqui é a função "render
00:02:41Server Component". O resto disso era apenas TanStack Start normal. Você poderia substituir isso
00:02:46por apenas alguns dados JSON simples sendo retornados, e você os buscaria exatamente da mesma maneira. Eu também acho que
00:02:51essa implementação é muito explícita sobre onde seu código está realmente rodando. Você faz tudo
00:02:55dentro de uma Server Function, então fica bem claro que vai rodar no servidor, e também
00:02:59dentro do "renderServerComponent". Na verdade, acho que posso melhorar meu código de demonstração aqui simplesmente
00:03:03pegando as funções que estou rodando aqui em cima, que quero que rodem no servidor, colocando-as
00:03:07dentro da Server Function e, em seguida, simplesmente passando esses valores para meu componente "greeting" como props, para
00:03:12que agora meu componente "greeting" seja essencialmente apenas um componente simples que pode ser usado no
00:03:16cliente ou no servidor. Estou rodando toda a lógica que quero que rode no servidor dentro da minha
00:03:21Server Function aqui. Novamente, é bem explícito que isso vai rodar no servidor. Isso
00:03:25literalmente parece o oposto exato da lógica usada no Next.js, e eu amo isso.
00:03:30Isso significa que posso pensar no React de uma maneira normal, tudo voltado para o cliente primeiro, e adicionando Server Components
00:03:34por cima quando eu quiser. Mas e se eu quisesse usar um componente de cliente dentro do meu Server
00:03:38Component, ou seja, aninhado na árvore? Digamos que eu quisesse adicionar um botão de contador aqui que, toda vez que
00:03:43clicamos nele, apenas adiciona ao contador. Bem, se eu simplesmente tentar adicionar isso ao meu Server Component aqui com
00:03:47um onClick e também uma chamada "useState", você pode ver que ele quebra completamente. Ele diz que "useState" não é
00:03:52uma função ou seu valor de retorno não é iterável, e isso é porque "greeting" está sendo usado como um Server
00:03:56Component. Não podemos usar essa funcionalidade de cliente. Para corrigir isso, você tem duas opções, e a segunda
00:04:01opção é definitivamente a melhor para usar, mas a primeira opção vai parecer familiar para aqueles
00:04:05de vocês que usaram o Next.js. Podemos simplesmente mover essa lógica para seu próprio componente e então usar a
00:04:10diretiva "use client". Isso funciona nos TanStack Start Server Components; podemos simplesmente usar o
00:04:14componente dentro do Server Component agora e, como você pode ver, está tudo funcionando bem. O lado negativo
00:04:18dessa abordagem, no entanto, é que agora temos um Server Component controlando a renderização de um
00:04:22componente de cliente, e isso pode começar a ficar um pouco bagunçado. Ou seja, se eu quisesse descobrir onde meu
00:04:28componente de contador estava na árvore, eu iria à minha rota aqui e veria que temos um Server
00:04:32Component "greeting", então voltaria ao Server Component "greeting" e, dentro dele, veria
00:04:37que temos um componente de cliente, e essa bagunça pode realmente começar a aumentar e apenas tornar esse limite
00:04:42entre servidor e cliente um pouco confuso. O TanStack não aceitaria isso, no entanto. Eles
00:04:47se perguntaram: e se o servidor não precisasse decidir cada parte da interface com formato de cliente?
00:04:51E isso os levou a criar algo totalmente novo: Composite Components. Para usar um, a primeira
00:04:56coisa que farei é remover nosso componente de cliente do nosso Server Component e simplesmente substituí-lo
00:05:00por qualquer children do nosso componente "greeting". Em seguida, também precisamos alterar o que estamos retornando da
00:05:05nossa Server Function aqui. Em vez de retornar um Server Component renderizável, precisamos retornar
00:05:09o que é chamado de "composite source". Para fazer isso, podemos usar nossa segunda função auxiliar de TanStack Server Component
00:05:14chamada "createCompositeComponent". Aqui dentro, estamos essencialmente construindo um componente
00:05:18onde as props aqui são consideradas os slots. Estou apenas usando um slot de "children" muito simples, então ele vai
00:05:22passar tudo o que colocarmos como um filho do meu Composite Component para estes "props.children", que
00:05:27estou passando para o componente "greeting" que acabamos de ter. Com isso, novamente, o que precisamos fazer é
00:05:31simplesmente buscar nosso Composite Component da nossa Server Function, e fazemos isso da mesma maneira no
00:05:36loader aqui. Você vê que estou apenas renomeando "source" para "greeting" e, em seguida, carregando-o com "useLoader
00:05:41Data". A única diferença aqui, no entanto, é que não podemos usar isso como um componente. Você vê que está lançando um
00:05:45erro aqui. Para renderizar um Composite Component, precisamos usar o componente auxiliar "CompositeComponent"
00:05:49que obtemos dos TanStack Server Components e, como a prop "source", passamos
00:05:53o Composite Component que buscamos da nossa Server Function que criamos anteriormente.
00:05:57Com isso, meu Server Component está renderizando como tínhamos antes, e também estou passando
00:06:01o contador como children deste Composite Component, e isso está sendo passado para o "greeting" onde
00:06:05o tínhamos configurado aqui, então está passando para os "props.children", para que tudo esteja funcionando
00:06:10bem. Também posso ir ao meu componente de contador e remover a diretiva que tínhamos aqui, já que isso
00:06:14não é mais necessário, uma vez que ele sabe que este será um componente de cliente, pois está dentro de um Composite
00:06:18Component. Isso está sendo usado como um slot. Agora, pode parecer que obtivemos o mesmo resultado
00:06:23lá, mas com mais trabalho do que apenas usar a diretiva "use client", mas o poder realmente vem da
00:06:27experiência do desenvolvedor, e é um pouco diferente do modelo "use client". Em vez de termos nosso
00:06:32servidor decidindo onde nossos componentes de cliente renderizam, como quando tínhamos o componente de contador dentro
00:06:36do próprio Server Component, em vez disso, o que estamos fazendo com os Composite Components é dizer: ei,
00:06:40haverá um slot aqui, vamos renderizar um componente de cliente, mas o próprio Server
00:06:44Component não tem ideia do que isso vai ser. Adicionamos isso depois em nosso código de cliente,
00:06:48então lidamos com todos os componentes baseados em cliente dentro do próprio código de cliente. Esse é apenas o começo também.
00:06:53Se dermos uma olhada em uma página mais complexa, como esta de post, onde este post está sendo renderizado no servidor,
00:06:58há dois problemas que quero resolver. O primeiro é que quero adicionar algumas ações, como
00:07:03curtir o post e seguir o autor, mas quero adicioná-las acima do título aqui e quero
00:07:08usar um componente de cliente. No momento, estou simplesmente usando esse padrão de slot de "children", o que significa que, se eu
00:07:12adicionasse minhas ações de post aqui embaixo, elas simplesmente iriam para onde estão os comentários, já que é assim que temos
00:07:17o componente configurado. Então, quero alguma maneira de dizer ao meu Server Component onde colocar componentes de cliente
00:07:22específicos. Então temos um segundo problema: se eu tiver um botão de "seguir autor", no momento, esta
00:07:27página de post não tem ideia de quem é o autor do post. Nós realmente descarregamos toda essa lógica no
00:07:32próprio Server Component. Se eu quisesse obter o autor em um componente de cliente aqui embaixo, eu teria
00:07:37que buscar o JSON do post e obter o autor dessa forma, e esse não é realmente um ótimo
00:07:42padrão, estaríamos buscando os dados duas vezes. Felizmente para nós, o TanStack realmente tem dois outros tipos de slot
00:07:46que podemos usar em um Composite Component além deste de "children", e o primeiro deles vai
00:07:50ser "render props". Isso é essencialmente apenas qualquer prop que seja uma função que retorna um elemento React, então
00:07:56isso pode ser chamado de qualquer coisa, não precisa ser chamado de "render actions". E aqui estou apenas dizendo quais
00:07:59dados quero que o Server Component passe adiante, e isso será o ID do post e o ID do autor.
00:08:04Agora, tudo o que precisamos fazer em nosso Composite Component é simplesmente usar essa função que estamos passando
00:08:08adiante como uma prop, onde quer que o componente que eventualmente será renderizado deva estar.
00:08:12No meu caso, quero que seja abaixo do cabeçalho do cartão, então posso chamá-lo com "props.render
00:08:16actions". Podemos usar um "opcional", então se não for passado adiante, não quebra, ele simplesmente não
00:08:20renderizará. Então também podemos passar adiante as informações que queremos do Server Component
00:08:24para nosso componente de cliente. Depois disso, nosso Composite Component vai aceitar a prop "renderActions"
00:08:28que acabamos de criar e, para o valor, simplesmente passamos uma função que tem um "postId"
00:08:32e um "authorId" como argumento, que o servidor vai preencher. Então simplesmente renderizamos nosso
00:08:36componente de cliente "postActions" e podemos passar esses dados através das props. Então agora tenho um botão
00:08:41aqui em cima onde posso curtir e copiar o link para o post e também clicar em "seguir autor" aqui, onde
00:08:45ele está ciente do nome do autor, apesar do fato de que nunca o busquei nesta página.
00:08:49Eu só o busco no Server Component, e o Server Component está passando esses dados para o
00:08:53componente de cliente para mim. Agora, você pode pensar que isso quebra a lógica que tínhamos antes, onde dissemos
00:08:57que não queremos que nenhum Server Component seja responsável por renderizar os de cliente, mas não quebra, e isso
00:09:01é porque os slots são, na verdade, opacos. O Server Component aqui em cima não tem ideia do que há dentro
00:09:06disso, ele apenas sabe que algo vai aqui e que precisa passar esses valores, que em
00:09:10este caso são o "postId" e o "authorId". Esta função não roda no servidor; em vez disso, o
00:09:15servidor simplesmente vê que precisa passar os dados adiante e, então, lá embaixo, no nosso cliente, é quando
00:09:19a função é realmente executada e o componente é renderizado. A mesma coisa exata também se aplica ao
00:09:23nosso terceiro tipo de slot, que é "component props"; este é, na verdade, um pouco mais simples que as
00:09:28render props. Tudo o que estamos fazendo é, em vez de ter uma função que então retorna nosso componente de cliente,
00:09:33estamos apenas passando o componente de cliente como uma prop, então em nosso Composite Component
00:09:38definido aqui em cima, estamos dizendo que queremos aceitar uma prop que é um componente React que tem
00:09:42as props de "postId" e "authorId", então podemos usar isso dentro do próprio componente. Você pode pensar em
00:09:47component props como um placeholder; o Server Component sabe que vai haver um componente
00:09:51lá que precisa de alguns dados, no nosso caso o "postId" e o "authorId", mas não se importa com o que
00:09:56é esse componente, desde que ele aceite essas props. Então, alterei meu componente "postActions" aqui embaixo para
00:10:01outro que fiz chamado "fakePostActions", e então, ao salvar isso, você pode ver que isso
00:10:05ainda vai renderizar porque é o cliente que é responsável por renderizar esse componente;
00:10:10é apenas o servidor que fornece os dados. Olhando para a documentação, não parece haver
00:10:14nenhuma diferença real na abordagem que você escolhe, seja com component props ou render props;
00:10:18pode ser apenas uma questão de preferência. A única diferença que consigo ver é que talvez você queira
00:10:22modificar os dados que recebe do servidor, então, neste caso, podemos fazer o que quisermos com
00:10:26o "postId" e o "authorId", já que é apenas uma função, e então podemos passar isso para nosso componente.
00:10:31Enquanto que, se você estiver usando component props, você apenas passa o componente em si e o servidor
00:10:36lida com a passagem das props. Agora, esses são os conceitos básicos dos TanStack Server Components, mas ainda há
00:10:40muito mais para gostar. Por exemplo, se você quisesse que a maior parte da sua página fosse renderizada no servidor,
00:10:44talvez você tenha um componente de cabeçalho, componente de conteúdo e um de rodapé, e você quer que todos sejam renderizados
00:10:49no servidor, você não precisa agrupar todos eles em uma única função "renderServerComponent". Você pode, na verdade,
00:10:53usar "Promise.all", dividi-los em três funções diferentes e, em seguida, simplesmente retorná-los como um objeto
00:10:58de uma única Server Function. Mas e se um desses componentes demorar para carregar? Isso significaria que
00:11:03a Server Function inteira demoraria, e, portanto, a página inteira. Bem, não se preocupe com isso também.
00:11:07O que podemos fazer, na verdade, é que, em vez de aguardar a função "renderServerComponent",
00:11:12podemos, na verdade, retornar a promise que ela cria e, então, no cliente, podemos aproveitar o hook "use" e
00:11:16limites de suspense para carregar esqueletos, então os Server Components serão carregados apenas quando estiverem prontos.
00:11:21Eu realmente gosto da abordagem que o TanStack adotou aqui. Não parece intrusiva, não sou forçado
00:11:25a adotá-la e posso adotá-la sem nenhuma solução alternativa estranha. Além disso, quando realmente vou usá-los,
00:11:31os próprios Server Components são, na verdade, apenas três novas funções; o resto é apenas simples
00:11:36Server Functions do TanStack Start, algo que eu já estava usando, e é tão simples quanto buscar
00:11:41dados. Isso também significa que ele se integra bem com ferramentas como TanStack Query, algo que eu
00:11:45definitivamente farei, e também simplifica coisas como o cache. Se você quisesse, poderia literalmente
00:11:49apenas colocar em cache a resposta da requisição GET na sua CDN. Definitivamente vou explorar mais isso,
00:11:54então me diga nos comentários abaixo o que você acha deles e se você gostaria de ver mais vídeos sobre eles.
00:11:59Bem, sim, inscreva-se e, como sempre, nos vemos no próximo.

Key Takeaway

O TanStack Start introduz uma abordagem de Server Components mais flexível que permite tratar renderização no servidor como uma requisição de dados granular e explícita, utilizando Composite Components para delegar interatividade ao cliente sem confusão de limites.

Highlights

O TanStack Start implementa Server Components focados na granularidade de requisições de dados, evitando o modelo 'server-first' rigoroso do Next.js.

A função renderServerComponent transforma componentes React em requisições GET renderizáveis no servidor, permitindo acesso direto a APIs de sistema como os.hostname.

Composite Components permitem delegar a renderização de componentes de cliente para o próprio cliente, evitando que o servidor tome decisões sobre a interface interativa.

Slots do tipo 'render props' e 'component props' facilitam a passagem de dados do servidor para componentes de cliente sem a necessidade de múltiplas chamadas de API.

A integração com o hook 'use' e limites de suspense permite o carregamento assíncrono de partes da página, evitando o bloqueio da renderização total pela latência de um único componente.

A arquitetura dos TanStack Server Components baseia-se em três funções auxiliares: renderServerComponent, createCompositeComponent e CompositeComponent.

Timeline

Abordagem explícita e granular

  • A implementação do TanStack evita o modelo onde todo o aplicativo orbita em torno de Server Components.
  • Server Functions permitem executar lógica de servidor e retornar componentes renderizáveis como requisições GET.
  • A distinção entre código de servidor e cliente é explícita, facilitando a depuração e o entendimento do fluxo de dados.

Ao contrário da diretiva 'use client' do Next.js, o TanStack trata Server Components com a mesma granularidade de uma busca de JSON. A função renderServerComponent recebe um componente React e retorna uma versão renderizável. Isso permite acessar funções do Node.js, como os.hostname, diretamente no servidor antes de enviar o resultado para o cliente via loader.

Composite Components e delegação de interatividade

  • Componentes de cliente não precisam mais de diretivas explícitas quando inseridos como slots em Composite Components.
  • Composite Components separam a definição da estrutura do servidor da renderização real no cliente.
  • A árvore de renderização permanece limpa ao evitar o aninhamento complexo entre servidor e cliente.

Para evitar a complexidade de misturar componentes de cliente dentro de Server Components, o TanStack criou os Composite Components. Eles funcionam como contêineres opacos onde o servidor define um slot (como children), e o cliente decide como renderizar o conteúdo interativo dentro desse espaço. Essa técnica mantém os limites entre cliente e servidor bem definidos e intuitivos.

Slots avançados e renderização assíncrona

  • Render props e component props permitem passar dados do servidor para slots sem requisições duplas.
  • A passagem de dados via slots opacos garante que o servidor forneça as informações necessárias sem ditar a implementação do componente cliente.
  • O uso de Promise.all e do hook 'use' possibilita a renderização assíncrona de componentes individuais para melhorar a performance da página.

O sistema de slots evoluiu para incluir render props e component props, permitindo que o servidor injete dados como postId e authorId diretamente em componentes cliente. Para evitar latência, múltiplos componentes podem ser carregados via Promise.all, com o cliente utilizando o hook 'use' e limites de suspense para exibir esqueletos (skeletons) enquanto o conteúdo é processado de forma independente.

Community Posts

No posts yet. Be the first to write about this video!

Write about this video