Solid.js: o framework que leva signals a sério

1. O que é Solid.js e por que ele se destaca

Nos últimos anos, o ecossistema de frameworks JavaScript passou por uma saturação de abordagens baseadas em Virtual DOM. React consolidou o modelo, Vue o refinou, e Svelte propôs uma compilação que elimina o runtime. Nesse cenário, Solid.js emerge como uma alternativa radical: reatividade granular sem Virtual DOM, com desempenho comparável a código vanilla.

A filosofia central de Solid.js é simples: em vez de reconciliar árvores de componentes, ele rastreia dependências em nível de sinal. Cada signal sabe exatamente quais partes do DOM dependem dele e atualiza apenas aquelas. Em benchmarks como o JS Framework Benchmark, Solid consistentemente ocupa as primeiras posições em tempo de inicialização, uso de memória e tempo de atualização, superando React e Vue por margens significativas.

2. Signals: o coração da reatividade em Solid

Um signal em Solid é um par getter/setter que carrega consigo um sistema de rastreamento de dependências. Quando você lê um sinal dentro de um createEffect ou createMemo, o Solid registra automaticamente essa dependência. Quando o sinal é alterado, apenas os efeitos e memos que dependem dele são reexecutados.

Exemplo prático — um contador reativo:

import { createSignal } from 'solid-js';

function Contador() {
  const [count, setCount] = createSignal(0);

  return (
    <div>
      <p>Valor: {count()}</p>
      <button onClick={() => setCount(c => c + 1)}>+1</button>
    </div>
  );
}

Diferente de React, onde count seria um estado imutável e o componente inteiro re-renderizaria, em Solid apenas o nó <p> é atualizado. O botão, o contêiner <div> e qualquer outro elemento permanecem intactos.

3. Computações derivadas e efeitos colaterais

Solid oferece dois mecanismos principais para computações reativas: createMemo e createEffect.

createMemo cria um valor derivado que é calculado preguiçosamente e cacheado. Ele só recalcula quando suas dependências mudam, e apenas se alguém o lê. Exemplo:

const [a, setA] = createSignal(2);
const [b, setB] = createSignal(3);
const soma = createMemo(() => a() + b());

console.log(soma()); // 5
setA(10);
console.log(soma()); // 12 (recalculado automaticamente)

createEffect executa uma função sempre que suas dependências mudam, tipicamente para efeitos colaterais como manipulação de DOM ou chamadas a APIs:

createEffect(() => {
  console.log(`A soma mudou para ${soma()}`);
});

A diferença crucial: memo é puro, cacheado e só executa quando lido; effect executa imediatamente após cada atualização reativa. Usar effect para computações derivadas é um antipadrão que prejudica performance.

4. Ciclo de vida e gerenciamento de estado

Solid fornece hooks de ciclo de vida que lembram componentes de classe, mas com sintaxe funcional:

  • onMount — executa uma vez quando o componente é montado
  • onCleanup — registra funções de limpeza executadas na desmontagem
  • onError — captura erros em computações reativas

Para estado assíncrono, createResource gerencia loading, erro e dados automaticamente:

const [user] = createResource(() => fetchUser(id()));

return (
  <Switch>
    <Match when={user.loading}>Carregando...</Match>
    <Match when={user.error}>Erro: {user.error.message}</Match>
    <Match when={user()}>
      <p>Nome: {user().name}</p>
    </Match>
  </Switch>
);

Para estado global, createStore oferece reatividade imutável com suporte a caminhos aninhados:

const [state, setState] = createStore({
  user: { name: 'João', preferences: { theme: 'dark' } }
});

setState('user', 'preferences', 'theme', 'light');

5. Reatividade no DOM: JSX sem Virtual DOM

Solid compila templates JSX diretamente para chamadas de DOM reais. Um elemento <div> vira document.createElement('div'), e cada expressão reativa vira uma chamada de atualização granular. Não há árvore virtual, diffing ou reconciliação.

Os componentes de controle de fluxo são nativamente reativos:

<Show when={loggedIn()} fallback={<LoginButton />}>
  <Dashboard />
</Show>

<For each={items()}>{(item, index) =>
  <li data-index={index()}>{item.name}</li>
}</For>

<Switch>
  <Match when={status() === 'loading'}>Carregando...</Match>
  <Match when={status() === 'error'}>Erro!</Match>
  <Match when={status() === 'success'}>Pronto!</Match>
</Switch>

Enquanto React precisaria reconciliar toda a árvore ao mudar de loading para success, Solid simplesmente remove o nó antigo e insere o novo, sem tocar no restante do DOM.

6. Composição e padrões avançados com Signals

Signals em Solid não estão amarrados a componentes. Você pode criar signals dentro de funções puras e compô-los livremente:

function createTimer() {
  const [time, setTime] = createSignal(0);
  const interval = setInterval(() => setTime(t => t + 1), 1000);
  return [time, () => clearInterval(interval)];
}

Para contexto reativo, createContext e useContext funcionam de forma similar ao React, mas sem re-renderizações desnecessárias:

const ThemeContext = createContext('light');

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Button />
    </ThemeContext.Provider>
  );
}

function Button() {
  const theme = useContext(ThemeContext);
  return <button class={theme}>Clique aqui</button>;
}

Padrões de stores modulares separam lógica de UI e estado, facilitando testes e manutenção.

7. Ecossistema e casos de uso reais

O ecossistema Solid inclui:

  • Solid Router — roteamento declarativo com lazy loading
  • Solid Start — meta-framework com SSR, islands architecture e deploy em edge
  • TypeScript — tipagem forte para signals, stores e recursos assíncronos

Projetos em produção incluem dashboards financeiros com atualizações em tempo real, editores de código como o CodeSandbox (que usa Solid para partes críticas de performance), e aplicações de streaming de dados. O baixo uso de memória torna Solid ideal para dispositivos com recursos limitados e para componentes que precisam ser atualizados centenas de vezes por segundo.

8. Solid.js no contexto dos 1200 temas: o que aprender com ele

Solid.js oferece lições valiosas para qualquer desenvolvedor frontend:

  1. Reatividade sem Virtual DOM — prova que a reconciliação de árvore não é o único caminho para UIs reativas
  2. Signals como padrão emergente — a proposta TC39 para signals nativos no JavaScript mostra que a indústria está migrando para esse modelo
  3. Trade-offs reais — Solid é excelente para aplicações com muitas atualizações granulares, mas React ainda vence em ecossistema maduro e ferramentas de debugging

Escolha Solid.js quando precisar de máxima performance em atualizações frequentes, baixo consumo de memória, ou quando quiser aprender um modelo de reatividade mais próximo dos fundamentos da linguagem. Para aplicações com estado global complexo e muitos desenvolvedores, React ou Vue podem ser mais produtivos devido à maturidade das ferramentas.

A lição final: o futuro dos frameworks não está em abstrair o DOM, mas em entender profundamente como dados fluem através do sistema. Solid.js nos mostra esse caminho com clareza e elegância.

Referências