Principais pontos para começar com o Remix

Apr 11, 2022

Remix é um novo framework para React que será uma grande tendencia em breve. Nesse artigo tentarei focar em pontos chaves, para que você consiga construir um modelo mental para começar a desenvolver.

Primeiramente, o que é Remix?

Remix é um framework React focado em Server Side Rendering. Isso significa que teremos um servidor que irá renderizar nosso HTML e servir para o cliente. Nesse caso não precisaríamos, por exemplo, de nada do Javascript pela parte do cliente para servir a página. O Javascript client-side servirá apenas para o que chamamos de progressive enhancement, para melhorar a experiencia de usuário quando necessário.

Em quais pontos devo focar

  • Criar um projeto;
  • Convenções principais para criar uma página com Remix;
  • Entender que tipos de conhecimentos prévios você pode aproveitar;

Começar um projeto é fácil?

Sim! Para começar seu projeto o framework te da várias opções, desde adapters para cada hospedagem que você precisar até templates já prontos! Esses templates você pode clicar aqui para saber mais.

Caso você queira criar um projeto limpinho, o comando é simples:

npx create-remix@latest

Você vai configurar coisas essenciais pelo próprio terminal.

Convenções do Remix

Todas as convenções do Remix são muito simples e muito intuitivas de se entender. As principais delas são:

  • Sua página será o componente React exportado por padrão com export default;
  • loader: parte responsável por recuperar dados de um banco de dados/APIs e retornar para nossa rota (roda apenas na parte do servidor). Além disso, é possivel configurar headers, jogar erros e servir como resource route. Funciona como GET.;
  • action: responsável por processar dados vindos do cliente e envia-los para banco de dados/APIs (roda apenas na parte do servidor). Também tem comportamentos extras parecidos com o loader. Qualquer requisição que não seja um GET vai chamar sua action;
  • meta: responsável por criar meta tags para seu documento HTML;
  • headers: cada rota pode definir seus próprios headers HTTP como por exemplo o header Cache-Control;
  • links: você pode definir por rota quais elementos <link> adicionar;
  • CatchBoundary: componente React que deverá ser exportado para tratar algum estado de erro do seu loader ou action;
  • ErrorBoundary: componente React que deverá ser exportado para tratar algum erro de rota, renderização ou carregamento de dados. Esse é um caso diferente de erros que são facilmente recuperáveis como por exemplo erros 404.
export const loader: LoaderFunction = () => {
  // código do seu loader
}

export const action: ActionFunction = () => {
  // codigo da sua action
}

export const headers: HeadersFunction = () => {
  // retorne seus headers aqui
}

export const meta: MetaFunction = () => {
  // retorne suas meta tags aqui
}

export const links: LinksFunction = () => {
  // retorne seus elementos para o <link> aqui
}

// https://remix.run/docs/en/v1/api/conventions#catchboundary
export const CatchBoundary = () => {
  const caught = useCatch();

  return (
    <div>
      <h1>Caught</h1>
      <p>Status: {caught.status}</p>
      <pre>
        <code>{JSON.stringify(caught.data, null, 2)}</code>
      </pre>
    </div>
  )
}

// https://remix.run/docs/en/v1/api/conventions#errorboundary
export const ErrorBoundary = ({ error }) => {
  return (
    <div>
      <h1>Error</h1>
      <p>{error.message}</p>
      <p>The stack trace is:</p>
      <pre>{error.stack}</pre>
    </div>
  )
}

const SuaPagina = () => {
  // hook usado para acessar dados do loader.
  // exportado pelo pacote do remix
  const data = useLoaderData()

  return <div>Sua pagina</div>
}

export default SuaPagina

Para evitar entrar em muitos detalhes nesse artigo sobre cada um desses pontos, você pode conferir com detalhes aqui.

Como aproveitar conhecimentos prévios?

Se você é da cena de backend, ou até mesmo já teve um contato com backend usando por exemplo express, você vai ter um tempo bem fácil com Remix.

Como explicado anteriormente, loader e action funcionam como rotas HTTP que sua própria página vai acessar. O time do Remix fez com que ambas funções sejam simples como uma rota HTTP deve ser.

Vou exemplificar usando um código simples do express. Imagine que você tenha uma rota de usuários:

// cria rota
app.get('/users', async (req, res) => {
  // acessa banco de dados e retorna usuários
  const users = await db.getUsers()

 // configura header
  res.set('Cache-Control', 'public, max-age=300, s-maxage=600')

  if (users.length === 0) {
    // joga um erro
    return res.status(404).send('No users found')
  }

  // retorna para o endpoint
  return res.send(users)
})

A transcrição para um loader no Remix fica quase idêntico:

import type { LoaderFunction } from '@remix-run/node'
import { json } from '@remix-run/node'
// todo arquivo com a extensão .server será servido
// apenas no lado do servidor, excluindo completamente
// o conteudo da build do client
import db from '~/utils/db.server'

export const loader: LoaderFunction = async () => {
   // acessa banco de dados e retorna usuários
  const users = await db.getUsers()

  // configura header
  const headers = {
    'Cache-Control': 'public, max-age=300, s-maxage=600',
  }

  if (users.length === 0) {
    // joga um erro
    throw json('No users found', { headers })
  }

  // retorno do loader
  // a função json do Remix é um helper
  // para o retorno tanto erro quanto retorno final
  return json(users, { headers })
}

E olha que bacana, se acessarmos a URL nesse momento, sem criar um componente para a rota, iremos perceber que a rota funciona como uma rota de API! Isso pode ser útil em alguns casos.

Mas e agora? Como é feito para acessar o dado do loader dentro do nosso componente? Simples! O Remix exporta um hook chamado useLoaderData.

import type { LoaderFunction } from '@remix-run/node'
import { json } from '@remix-run/node'
import { useLoaderData } from '@remix-run/react'
import db from '~/utils/db.server'

export const loader: LoaderFunction = async () => {
  const users = await db.getUsers()

  const headers = {
    'Cache-Control': 'public, max-age=300, s-maxage=600',
  }

  if (users.length === 0) {
    throw json('No users found', { headers })
  }

  return json(users, { headers })
}

const UserTestPage = () => {
  // acessa dados do loader
  const users = useLoaderData()

  return (
    <ul>
      {users.map((user) => (
        <li key={user.name}>{user.name}</li>
      ))}
    </ul>
  )
}

export default UserTestPage

Leia sobre como tratar erros do loader com CatchBoundary

Quis trazer um exemplo para o loader nesse artigo. As action não são muito diferentes. Você pode ver um exemplo clicando aqui.

Conclusão

Remix traz pontos muito fortes gerais de web fundamentals. Logo, muitos dos conhecimentos que já temos de web poderão ser reutilizados de forma simples com Remix.