No final de 2020, a equipe do React introduziu o conceito de "Componentes de Servidor(Server Components) React com Tamanho de Pacote Zero". Desde então, a comunidade de desenvolvedores do React tem experimentado e aprendido como aplicar essa abordagem futurista.
O React mudou a forma como pensamos em construir interfaces de usuário. E o novo modelo usando Componentes de Servidor(Server Components) React é muito mais estruturado, conveniente, e fácil de manter, oferecendo uma experiência de usuário melhor.
A última versão do Next.js adotou a abordagem "Pensando em Componentes de Servidor". E como desenvolvedores React, precisamos nos adaptar a esse novo modelo mental para aproveitar totalmente seu poder na construção de aplicativos.
Neste tutorial, você aprenderá sobre os Componentes de Servidor(Server Components) React (RSC). Você aprenderá exatamente o que são e como funcionam, e, mais importante, qual problema eles resolvem.
Também mostrarei muitos exemplos para que você possa entender por que precisamos de RSC. Por fim, você aprenderá a diferença entre Componentes de Servidor(Server Components) React e outra funcionalidade semelhante, mas com nome diferente, chamada Renderização no Lado do Servidor (SSR).
Se você é novo no React, será necessário ter algum conhecimento básico sobre arquitetura de componentes, estado, passagem de dados usando props e a árvore do DOM virtual antes de aprender sobre os componentes de servidor(Server Components) React.
React como uma Biblioteca de Interface de Usuário no Lado do Cliente
Desde o seu início, o React tem sido uma biblioteca de interface de usuário no lado do cliente. É uma biblioteca de código aberto baseada em JavaScript que ajuda desenvolvedores web e móveis a construir aplicativos que utilizam uma arquitetura baseada em componentes.
A filosofia do React sugere que dividamos todo o nosso design em pedaços menores e autocontidos chamados componentes.
Em seguida, os componentes podem ter seus próprios dados privados chamados estado e uma maneira de passar dados para outros componentes chamada props. Você divide esses componentes em uma hierarquia de componentes, define o estado, gerencia os efeitos que alteram o estado e decide o fluxo de dados.
Tradicionalmente, todos esses componentes são funções em JavaScript (estamos falando apenas de componentes funcionais aqui - deixaremos os componentes de classe no passado). Quando o aplicativo é carregado no navegador, baixamos o código do componente e tornamos o aplicativo funcional usando esses componentes.
Usaremos o termo "componentes" aqui. No entanto, como este artigo introduz o conceito de Componentes de Servidor(Server Components) React, chamaremos esses componentes tradicionais de "Componentes do Cliente" (pois são baixados no cliente/navegador e o React realiza sua mágica para renderizá-los).
Problemas Comuns em Aplicações React
Componentes de Cliente React são excelentes e funcionam bem para resolver certos casos de uso. No entanto, precisamos analisar o padrão de forma um pouco diferente ao construir aplicações React. Isso ocorre porque devemos nos preocupar com:
- Experiência do Usuário: Construímos produtos de software para nossos usuários e clientes. A experiência do usuário da aplicação é importante se queremos que o aplicativo seja bem-sucedido.
- Manutenibilidade: O código do projeto deve ser bem mantido ao longo dos anos, por várias equipes de desenvolvimento.
- Custo de Desempenho: A aplicação não deve ser lenta, e nossa abordagem de design não deve torná-la mais lenta.
Vamos agora ver alguns exemplos de problemas comuns que você pode encontrar. Também entenderemos como podemos implementar e projetar para cada um desses pontos-chave em nosso desenvolvimento web diário usando o React.
O Problema de Layout Shift
Um problema muito comum de experiência do usuário é o Layout Shift repentino quando um componente é renderizado. Vamos dar uma olhada no trecho de código abaixo:
<CourseWrapper> <CourseList /> <Testimonials /> </CourseWrapper>
Este é um código JSX familiar, onde temos um componente CourseWrapper e dois componentes filhos, CourseList e Testimonials. Vamos supor que tanto CourseList quanto Testimonials façam chamadas de rede (chamadas de API) para buscar os dados.
Aqui está o componente CourseList:
function CourseList() { // Suponha uma chamada de rede, na vida real // você a lidará com useEffect const info = fetchCourseList(); return ( <> </> ) }
E o componente Testimonials:
function Testimonials() { // Suponha uma chamada de rede, na vida real // você a lidará com useEffect const info = fetchTestimonials(); return ( <> </> ) }
Como esses componentes fazem chamadas de rede, não há garantia sobre a sequência em que as respostas podem retornar. Isso depende da velocidade da rede, latência e muitos outros fatores.
Em uma situação em que a chamada de rede para o componente Testimonials é concluída antes do componente CourseList, o componente Testimonials será renderizado primeiro e, em seguida, o componente CourseList será renderizado. Isso fará com que o componente Testimonials seja empurrado para se ajustar à nova renderização. Isso pode causar uma experiência de usuário desagradável, especialmente se a página ficar saltando ou piscando à medida que os componentes são renderizados.
Podemos melhorar um pouco a experiência dos usuários usando um indicador de carregamento ou um efeito de brilho, informando aos usuários que algo está a caminho (mas não temos certeza de quando).
O Problema da Cascata de Rede(Network Waterfall)
Vamos discutir outro problema típico de experiência do usuário. Imagine um componente React semelhante ao que tínhamos no exemplo anterior:
function Course() { return( <CourseWrapper> <CourseList /> <Testimonials /> </CourseWrapper> ) }
Agora, vamos fazer uma pequena alteração. Além dos componentes CourseList e Testimonials, o CourseWrapper agora também faz uma chamada de rede.
function CourseWrapper() { // Suponha uma chamada de rede, na vida real // você a lidará com useEffect const info = fetchWrapperInfo(); return( <> </> ) }
Neste caso, o componente pai faz uma chamada de rede para buscar dados, e ambos os seus componentes filhos também fazem chamadas de rede.
Agora, a coisa interessante é que o componente pai não será renderizado até que sua chamada de rede seja concluída. Ele também impedirá a renderização de seus componentes filhos.
Esse fenômeno em que esperamos que a resposta da coisa anterior seja concluída para começar na coisa atual é conhecido como cascata. Neste caso, temos problemas de Cascata de Rede e Cascata de Renderização de Componentes.
Agora, você pode pensar em remover todas essas chamadas de rede de cada componente e movê-las para uma única chamada para que os componentes individuais não precisem esperar pela resposta. Isso é uma jogada inteligente, mas pode causar um problema de manutenção. Vamos aprender mais sobre isso na próxima seção.
Problemas de Manutenção
Agora que examinamos alguns problemas de experiência do usuário relacionados às interações no lado do servidor, consideremos um problema de manutenção.
Vamos supor que nenhum dos nossos componentes faça chamadas de rede. Buscamos todos os detalhes de todos os componentes (sim, incluindo o componente pai) de uma vez usando uma única chamada de API fetchAllDetails().
Após isso, passamos as informações necessárias para cada um dos componentes como props. Isso é melhor do que o problema da "Cascata" que vimos anteriormente, certo?
function Course() { // Suponha uma chamada de rede, na vida real // você a lidará com useEffect const info = fetchAllDetails(); return ( <CourseWrapper info={info.wrapperInfo} > <CourseList info={info.listInfo} /> <Testimonials info={info.testimonials} /> </CourseWrapper> ) }
Mas isso pode causar alguns problemas de manutenção para nós.
Vamos supor que um belo dia, a equipe de produtos decide remover a funcionalidade de Testimonials. Portanto, podemos simplesmente ir e remover o componente Testimonials do código acima. Isso funciona! No entanto, podemos esquecer de limpar os dados que obtemos usando a chamada fetchAllDetails(). Eles podem permanecer desnecessariamente sem serem usados.
Para mitigar isso, você pode acabar alterando seu código de uma maneira que já discutimos nas seções anteriores, explicando os possíveis problemas de experiência do usuário. Portanto, precisamos encontrar uma solução melhor. Mas antes disso, vamos falar sobre mais uma consideração, o Custo de Desempenho.
Custos de Desempenho
A última área problemática que discutiremos são os custos de desempenho.
Tradicionalmente, os componentes React são funções JavaScript no lado do cliente. Eles são os blocos de construção para sua aplicação React. Quando carregamos a aplicação no cliente, os componentes são baixados no cliente e o React executa o que é necessário para renderizá-los para você.
Mas isso vem com dois problemas significativos:
Primeiro, quando o usuário envia a solicitação, o aplicativo faz o download do HTML junto com o JavaScript vinculado, CSS e outros recursos, como imagens.
No lado do cliente (no navegador), o React inicia sua mágica e "hidrata" a estrutura HTML. Ele analisa o HTML, anexa os ouvintes de eventos ao DOM e busca os dados no armazenamento. Assim, o site se torna um aplicativo React totalmente operacional.
No entanto, o ponto é que há muito acontecendo no cliente. Acabamos fazendo o download de todo esse código no cliente.
Na maioria das vezes, precisamos de bibliotecas externas (módulos Node) como dependências para nosso projeto. Todas essas dependências serão baixadas no lado do cliente, tornando-o ainda mais pesado.
Agora que você entende os problemas, provavelmente vai apreciar o que os Componentes de Servidor(Server Components) React oferecem e como eles podem lidar com esses problemas.
Mas antes de falarmos sobre isso, vamos entender um pouco mais sobre o cliente e o servidor.
O Modelo Cliente-Servidor
Usamos os termos cliente e servidor muitas vezes neste artigo. Portanto, vamos fornecer uma definição formal deles e explicar sua relação em um nível mais elevado.
Cliente: Um cliente, em relação a uma aplicação, é um sistema que executa as tarefas no lado do usuário final. Exemplos de clientes incluem seu PC, notebook, dispositivo móvel, navegador, e assim por diante.
Servidor: Um servidor, como o nome sugere, fornece serviços aos clientes. Ele pode estar localizado junto a um armazenamento de dados ou banco de dados para acesso rápido aos dados.
Solicitação(Request): Uma solicitação é um modo de comunicação que um cliente usa para pedir um serviço a um servidor.
Resposta(Response): Uma resposta também é um modo de comunicação que um servidor usa para enviar de volta o serviço (dados/informações) ao cliente.
Componentes do Cliente(Client Components) React
Como mencionei anteriormente, tradicionalmente, os componentes React vivem no lado do cliente. Quando eles interagem com um servidor, eles enviam uma solicitação e aguardam a resposta retornar. Ao receber uma resposta, o componente do cliente aciona o próximo conjunto de ações.
Se o serviço solicitado for concluído com sucesso, o componente do cliente age de acordo com a interface do usuário e exibe uma mensagem de sucesso. Em caso de erro, o componente do cliente informa isso aos usuários.
Quando isso causa uma Cascata de Rede, a resposta do componente do cliente fica atrasada e causa uma experiência de usuário ruim. Então, como podemos mitigar isso?
Como os Componentes de Servidor(Server Components) React (RSCs) Ajudam
Que tal movermos nossos componentes React para o servidor? E talvez os coloquemos junto ao armazenamento de dados... mas isso é mesmo possível?
Sim! Vamos agora conhecer os Componentes de Servidor React. Esses novos componentes podem buscar dados mais rapidamente, pois estão no servidor. Eles têm acesso à infraestrutura do seu servidor, como sistemas de arquivos e armazenamento de dados, sem fazer nenhuma viagem pela rede.
Isso representa uma mudança completa de paradigma para os desenvolvedores React, pois agora precisamos pensar em termos de componentes do servidor.
Com os RSCs, você pode mover sua lógica de busca de dados para o servidor (para que seu componente busque os dados sem uma chamada de rede) e deixá-lo pronto no próprio servidor. Os dados que retornam para o cliente são um componente bem construído com todos os dados incorporados nele. Incrível, não é?
Isso significa que, usando componentes de servidor React, você pode escrever código como este:
import { dbConnect } from '@/services/mongo' import { addCourseToDB } from './actions/add-course' import CourseList from './components/CourseList' export default async function Home() { // Obter uma conexão com o MongoDB await dbConnect(); // Obter todos os cursos do banco de dados usando o modelo const allCourses = await courses.find(); // Isso é impresso no console do servidor console.log({allCourses}) return ( <main> <div> <CourseList allCourses={allCourses} /> </div> </main> ) }
Olhe para isso! Você pode notar algumas mudanças imediatamente:
- O componente é do tipo async, pois lidará com chamadas assíncronas.
- Conectamos ao banco de dados (MongoDB) diretamente do componente. Incrível! Normalmente, vemos esse tipo de código com Node.js ou Express, certo?
- Em seguida, consultamos o banco de dados e buscamos os dados para passar ao nosso JSX para renderização.
- Observe que o log no console será feito no console do servidor, não no console do navegador.
Além disso, nos livramos completamente da gestão de estado (useState) e da gestão de efeitos (useEffect). É limpo e simples.
Com os componentes de servidor React, você talvez não precise usar useEffect (nunca!).
Limitações dos Componentes de Servidor React (RSCs)
Com todas essas vantagens, os RSCs também têm algumas limitações que você deve ter em mente:
- Os RSCs permanecem no servidor e são renderizados no servidor. Eles não têm nada relacionado ao lado do cliente. Isso significa que você não pode adicionar nenhuma interatividade do usuário aos componentes do servidor. Por exemplo, você não pode usar nenhum manipulador de eventos ou React hooks como useState, useReducer, useEffect em seus componentes do servidor.
- Você não pode usar APIs da Web do navegador, como localstorage, bluetooth, web USB e assim por diante, em componentes do servidor.
- Para tudo o que está relacionado às interações do cliente, você deve continuar a usar componentes do cliente.
Faz sentido? Então, como você pode organizar melhor seus componentes para sua aplicação?
Como Usar Componentes do Cliente e do Servidor Juntos
Seu aplicativo pode ser uma combinação de componentes do servidor e do cliente. Você verá um exemplo em breve, mas primeiro, vamos entender o conceito.
Os componentes do servidor podem importar e renderizar componentes do cliente, mas os componentes do cliente não podem renderizar os componentes do servidor neles. Se você deseja usar um componente do servidor em um componente do cliente, pode passá-lo como props e usá-lo dessa maneira.
É melhor ter os componentes do servidor na raiz da hierarquia de componentes e empurrar os componentes do cliente para as folhas da árvore de componentes.
A busca de dados pode acontecer na parte superior nos componentes do servidor, e você pode passá-los da maneira que o React permite. As interações do usuário (manipuladores de eventos) e o acesso às APIs do navegador podem ser tratados nos componentes do cliente no nível das folhas.
RSCs não são a mesma coisa que a Renderização no Lado do Servidor (SSR)?
Não, eles não são. Tanto RSC quanto SSR têm a palavra "servidor" em seus nomes, mas a semelhança termina aí.
Com a Renderização no Lado do Servidor (SSR), enviamos o HTML bruto do servidor para o cliente, em seguida, todo o JavaScript do lado do cliente é baixado. O React inicia o processo de hidratação para transformar o HTML em um componente React interativo. Na SSR, o componente não permanece no servidor.
Agora sabemos que, com os componentes de servidor React, os componentes permanecem no servidor e têm acesso à infraestrutura do servidor sem fazer nenhuma viagem de rede.
O SSR é útil para carregar mais rapidamente a página inicial de sua aplicação. Você pode usar SSR e RSCs juntos em sua aplicação sem problemas.
Em resumo
- Os Componentes de Servidor React têm acesso ao backend sem fazer viagens pela rede.
- Podemos evitar cascata de rede usando Componentes de Servidor React.
- Os componentes de servidor React suportam divisão automática de código e melhoram o desempenho de seu aplicativo com tamanho de bundle zero.
- Como esses componentes estão no lado do servidor, eles não têm acesso a manipuladores de eventos do lado do cliente, estado e efeitos. Isso significa que você não pode usar nenhum manipulador de eventos ou React hooks como useState, useReducer e useEffect.
- Um componente de servidor React pode importar e renderizar um componente do cliente, mas o contrário não é verdadeiro. No entanto, você pode passar um componente de servidor como props para um componente do cliente.
Sugestões de cursos
Descubra o caminho para se tornar um especialista em programação web. Aprenda HTML, CSS, JavaScript e os principais frameworks nesta jornada emocionante. Com instrutores experientes e materiais práticos, você desenvolverá habilidades práticas para criar sites impressionantes e aplicativos interativos. Impulsione sua carreira na indústria de tecnologia e abra portas para oportunidades de emprego lucrativas. Garanta sua vaga hoje mesmo e inicie sua jornada para se tornar um desenvolvedor web de sucesso.
Método Para Aprender a Programar do Absoluto ZERO com Node.js, React e React Native.
As tecnologias ensinadas no curso são responsáveis por muitas vagas no mercado de trabalho.
Além da alta demanda, os salários vão de R$47.000,00 até R$197.000,00 anuais tendo empresas que possibilitam o trabalho remoto e até vagas Internacionais.
Para que você possa estar apto a preencher uma dessas vagas eu vou te apresentar o passo a passo para você se tornar um verdadeiro expert nessas tecnologias.
O curso te dará o passo a passo de como criar estruturar de um sistema do zero com Node.js, React e React Native.