Um guia de React Server Components
O que é React Server Components?
React Server Components é uma feature experimental do React, anunciada no blog do React.js em 21 de Dezembro de 2020.
No momento (25/02/2021) ainda não está pronto para produção.
O maior apelo do React Server Components, é mover componentes que não tem interação com o usuário do client para o server.
Outro problema encontrado é o número alto de requests feitos, enquanto os usuários esperam para a página ou os dados, estarem prontos, quando eles abrem a página.
Por exemplo uma forma de fazer fetch de dados hoje, é chamar uma API usando o hook useEffect
:
useEffect(() => {
axios.get("ENDEREÇO AQUI")
.then((response) => {
// set data into state
setData(response.data);
})
.catch((error) => {
console.log(error);
});
}, []);
Apesar de não ter nada de errado com isso, essa forma de pegar os dados sempre vai ter um custo de tempo para renderizar algo útil para o usuário.
O outro problema claro é sempre o tamanho do bundle. Minificação, code splitting, eliminação de código morto, são algumas técnicas utilizadas para reduzir o tamanho do bundle de uma aplicação React. Porque? Porque impacta diretamente na experiência do usuário, com carregamentos lentos.
Como funciona?
Como essa feature do React ainda está em fase de experimentação, recomendo assistir a live explicando o conceito do React Server Components, pois a implementação pode mudar com o tempo.
Pode também clonar o repositório demo para entender como funciona a feature
A primeira coisa que notamos são vários packages com versão experimental no arquivo package.json
:
"react": "0.0.0-experimental-3310209d0",
"react-dom": "0.0.0-experimental-3310209d0",
"react-fetch": "0.0.0-experimental-3310209d0",
"react-fs": "0.0.0-experimental-3310209d0",
"react-pg": "0.0.0-experimental-3310209d0",
"react-server-dom-webpack": "0.0.0-experimental-3310209d0",
Os pacotes react
, react-dom
e, react-server-dom-webpack
estão usando uma versão experimental que funcionam com o React Server Component, enquanto oreact-fetch
, react-fs
e react-pg
é um grupo de packages usado pela interação com o sistema de input/output ( chamado de libs do React IO)
Outro ponto importante é que o demo funciona em cima do Express.js, o que faz sentido, porque você precisa de um servidor para renderizar os componentes.Mas isso levanta a questão, Server Components apenas em ambientes Java Script? Nada de Go, Java, C# .NET, PHP?
Dentro da pasta src/
você tem três tipos de extensões nos arquivos:
.server.js
indica um Server Components.client.js
indica um React Client Components- O habitual
.js
, pode ser rodado no client ou no server. Dependendo de quem o importa. Também serve para Shared Components. Por exemplo se você importa um Shared Component no server, ele atuará como Server Component, se você importa ele no Client, Será um Client Component.
Quando você inicia sua aplicação com o comando npm start
, duas tarefas vão ser executadas simultaneamente:
- O servidor Node, rodando o script
server/api.server.js
- O build do Webpack rodando o bundle client-side do React, usando o script
scripts/build.js
Olhando o script do server podemos ver que o app.server.js
está sendo importado para o arquivo:
const ReactApp = require('../src/App.server').default;
e depois é processado com um Node Writable stream:
const {pipeToNodeWritable} =
require('react-server-dom-webpack/writer');async function renderReactTree(res, props) {
await waitForWebpack();
const manifest = readFileSync(
path.resolve(__dirname, '../build/react-client-manifest.json'),
'utf8'
); const moduleMap = JSON.parse(manifest);
pipeToNodeWritable(React.createElement(ReactApp, props), res, moduleMap);
}
Bundle de tamanho Zero
O código dentro da extensão .server.js
incluindo suas dependências , não é incluindo no bundle do client. Isso significa que tem zero efeito no tamanho do bundle.
As bibliotecas que importamos no código atualmente, serão lidas do lado do servidor, economizando no tamanho do bundle do client.
Client Component:
//markdown.jsimport marked from 'marked'; // 37.38KBimport sanitizeHtml from 'sanitize-html'; // 208 KBexport default function TextwithMarkdown({Text}) {
return(
<div className="text-markdown"
dangerousSetInnerHTML={{
__html: sanitizeHtml(marked(text)),
}}
/>
);
}
Server Component:
// markdown.server.jsimport marked from 'marked'; // 0KBimport sanitizeHtml from 'sanitize-html'; // 0KBexport default function TextwithMarkdown({Text}) {
return(
<div className="text-markdown"
dangerousSetInnerHTML={{
__html: sanitizeHtml(marked(text)),
}}
/>
);
}
Acesso do backend ao banco de dados:
Server Components tem acesso direto ao banco de dados ou ao filesystem no servidor. Você pode pegar qualquer dado que você quiser e mandar para o client, no primeiro render:
como você pode ver no exemplo do arquivo NoteList.server.js
:
export default function NoteList({searchText}) {
const notes = db.query(
`select * from notes where title ilike $1 order by id desc`,
['%' + searchText + '%']
).rows;
return notes.length > 0 ? (
<ul className="notes-list">
{notes.map((note) => (
<li key={note.id}>
<SidebarNote note={note} />
</li>
))}
</ul>
) : (
<div className="notes-empty">
{searchText
? `Couldn't find any notes titled "${searchText}".`
: 'No notes created yet!'}{' '}
</div>
);
}
Restrições
Assim como os Server Components são estáticos e renderizados no backend, eles tem algumas restrições de como podem ser usados.
- Primeiro, um Server Component não tem nenhuma interatividade (sem
useState()
,nemuseEffect()
).
Uma resolução para esse problema, seria importar Client Components, ( que podem ser interativos) , dentro de um Server Component, por exemplo você pode ter o melhor dos dois mundos:
//Page.server.js// import de componente interativo do client
import Button from '.Button.client.js';export default Page() {
// data fetch, processamento, outras coisas..return(
// use o <Button /> e outros componentes interativos aqui
);}
- Segundo,
props
de um Server Component para um Client Component tem que ser serializada na rede ( ex: passar dados como strings, JSON e JSX mas não podem passar em funções JavaScript) . Isso é porque os Server Components renderizados tem que ser enviados pela rede.
Rotas
Analizando o projeto no demo no github, as rotas são setadas direto no arquivo api.server.js, onde literalmente, você constroi uma api como quiser, algo que o Next.js também tem como feature, seja fazendo sua própria Api server-side, ou refletindo na pasta /api
Quais as vantagens?
No começo parece muito com Server-Side Rendering (SSR). Na verdade oferece vantagens muito similares:
- Desenvolvimento mais fácil, por ter acesso a recursos de servidor como : banco de dados,filesystem, micro serviços, etc.).
- Melhor performance, pois evitamos a latência de rede entre servidor e cliente.
- Menor tamanho do bundle. Libs que forem usados só no servidor não precisam ser usados no client : (como
loadash
,rambda
,moment
,e etc.), além dos Server Components nunca serem incluidos no bundle, e lidos pelo client. - Segurança: Fazer queries no lado do servidor sempre é mais seguro que no client. Além de não expor as requisições
- Pode fazer queries GraphQL
- Pode acessar diretamente bancos de dados PostgreSQL com o package
react-pg
Também oferece algumas vantagens para desenvolvedores React:
- Code splitting automático. (A técnica de dividir o código em bundles menores, para o cliente ler apenas o que é necessário) Atualmente, Desenvolvedores React tem que fazer um esforço para poder implementar o code splitting algo como:
const MyComponent = React.lazy(() => import('./MyComponent.js'));
no futuro os Server Components vai lidar com isso automaticamente. Isso significa que poderemos importar diretamente como já fazemos:
import MyComponent from './MyComponent.client.js';
Como os Server Components são diferentes de SSR (ex: Next.js)?
Utilizamos SSR hoje no React, apenas para que os componentes sejam renderizados como HTML no client, para que sua aplicação pareça ter uma resposta muito rápida. Seu usuário não consegue fazer nada na sua aplicação até que o JavaScript seja baixado.
React Server Components é diferente.Como mostrado no demo, Server Components não é renderizado como HTML mas como um stream de formato especial enviado ao client. O stream não tem um padrão de protocolo, mas se parece muito com json. Aqui um exemplo:
M1:{"id":"./src/SearchField.client.js","chunks":["client5"],"name":""}
No SSR , usamos apenas para o render inicial, Server Components podem puxar os dados multiplas vezes para fazer re-render ( no exemplo do demo, os posts de markdown)
Server Component x Client Components
Vamos considerar o exemplo de uma aplicação que tem uma barra de busca para filmes, linkada a um banco de dados do seu backend. Vamos considerar que os resultados são um React Server Component, não um component comum (client), no caso do client component isso iria ocorrer:
- Assim que você ,uma requisição
fetch
é feita para uma API para um payload JSON. - Assim que você recebe a resposta, você parseia o JSON, e manda pro React.
- O React renderiza os dados, e mostra as informações do filme na tela.
Simples , não? Com React Server Components acontece o seguinte:
- Uma requisição é feita para um backend que você é dono e é capaz de fazer um render RSC ( React Server Component)
- O componente é renderizado no próprio servidor, e você recebe um build do markup estático, sem ser JSON ou HTML , como abaixo: M1:{“id”:”./src/SearchField.client.js”,”chunks”:[“client5”],”name”:””}
- O frontend (client), renderiza esse markup, como um UI estático ( importante: não um componente React), isso salva processamento adicional do componente no front-end.
Conclusão
React Server Components é uma nova abordagem de como construímos nossas aplicações React.
Pode mudar toda forma como o mercado utiliza o framework, e abre um leque de possibilidades em questão de performance, segurança e organização de código.
Por enquanto, podemos apenas brincar e testar, não sendo recomendado usar em produção.
A thread oficial no github tem a discussão da feature e do conceito, e muitas pessoas estão participando dando sugestões, críticas e idéias, e você também pode participar. Sugiro caso conheça inglês também ler os artigos linkados abaixo.
Com conteúdo traduzido de:
Bits and Pieces — React Server Components por Nathan Sebhastian
An Introduction to React Server Components por Nilanth
React Server Components Explained por Mehul Mohan
Links Úteis:
React Server Components — Addy Osmani
React Server Components Github Demo
React Server Components Video Explicação
Post no Blog do React sobre React Server Components