Voltar para o Blog

Signals no JavaScript: A Proposta TC39 Que Pode Trazer Reatividade Nativa Para a Linguagem

Olá HaWkers, uma proposta que está ganhando momentum no TC39 pode mudar fundamentalmente como construímos interfaces reativas em JavaScript. Signals, um primitivo de reatividade já usado por Angular, Vue, Solid e Svelte, está sendo considerado para inclusão na especificação oficial do ECMAScript.

Se você trabalha com frontend moderno, essa proposta merece sua atenção. Vamos entender o que são Signals, como a proposta funciona e o que isso significa para o futuro do desenvolvimento web.

O Que São Signals

Signals são primitivos de reatividade que permitem criar valores que automaticamente notificam dependentes quando mudam. Pense neles como variáveis inteligentes que sabem quem as está usando.

O Conceito Básico

A ideia fundamental é simples mas poderosa:

// Pseudocódigo ilustrativo do conceito
const count = signal(0);        // Cria um signal com valor inicial 0
const doubled = computed(() => count.value * 2);  // Derivado automaticamente

console.log(doubled.value);     // 0
count.value = 5;                // Muda o signal
console.log(doubled.value);     // 10 - automaticamente atualizado

Por Que Signals Importam

Problemas que Signals resolvem:

  1. Atualizações granulares: Em vez de re-renderizar componentes inteiros, apenas o que depende do valor muda

  2. Sincronização automática: Valores derivados são sempre consistentes com suas fontes

  3. Performance previsível: O sistema sabe exatamente o que precisa atualizar

  4. Simplicidade de modelo mental: Menos conceitos que sistemas como Redux

A Proposta TC39

A proposta de Signals para JavaScript está sendo desenvolvida colaborativamente entre representantes de vários frameworks.

Quem Está Envolvido

Autores da proposta:

  • Rob Eisenberg (Microsoft/Angular)
  • Daniel Ehrenberg (Bloomberg/Igalia)
  • Contribuições de maintainers de Vue, Solid, Svelte

Objetivo declarado:

"Criar uma base comum de reatividade que permita interoperabilidade entre frameworks e reduza duplicação de código."

O Que a Proposta Inclui

A proposta define dois primitivos principais:

Signal.State:

// Signal básico que guarda um valor
const counter = new Signal.State(0);

// Lendo o valor
console.log(counter.get()); // 0

// Escrevendo o valor
counter.set(counter.get() + 1);
console.log(counter.get()); // 1

Signal.Computed:

const firstName = new Signal.State("John");
const lastName = new Signal.State("Doe");

// Computed que deriva de outros signals
const fullName = new Signal.Computed(() => {
  return `${firstName.get()} ${lastName.get()}`;
});

console.log(fullName.get()); // "John Doe"

firstName.set("Jane");
console.log(fullName.get()); // "Jane Doe" - atualizado automaticamente

O Que NÃO Está Na Proposta

Importante entender os limites:

Não incluído:

  • Sistema de efeitos (side effects)
  • Batching automático
  • Integração com DOM
  • Scheduling de atualizações

Por quê?

"A proposta é intencionalmente minimal para permitir que frameworks implementem suas próprias semânticas sobre a base comum."

Como Frameworks Já Usam Signals

Para entender a proposta, é útil ver como frameworks existentes implementam reatividade.

Angular Signals

O Angular adotou Signals oficialmente em versões recentes:

import { signal, computed, effect } from '@angular/core';

@Component({
  template: `
    <button (click)="increment()">
      Count: {{ count() }}
    </button>
    <p>Double: {{ doubleCount() }}</p>
  `
})
export class CounterComponent {
  count = signal(0);
  doubleCount = computed(() => this.count() * 2);

  increment() {
    this.count.update(value => value + 1);
  }

  constructor() {
    effect(() => {
      console.log(`Count changed to: ${this.count()}`);
    });
  }
}

Vue Reactivity

O Vue 3 usa um sistema similar chamado refs:

import { ref, computed, watchEffect } from 'vue';

const count = ref(0);
const doubled = computed(() => count.value * 2);

watchEffect(() => {
  console.log(`Count is now: ${count.value}`);
});

// Em template: {{ count }} e {{ doubled }}

SolidJS Signals

SolidJS foi pioneiro em signals para o frontend:

import { createSignal, createEffect, createMemo } from 'solid-js';

function Counter() {
  const [count, setCount] = createSignal(0);
  const doubled = createMemo(() => count() * 2);

  createEffect(() => {
    console.log(`Count changed to: ${count()}`);
  });

  return (
    <button onClick={() => setCount(c => c + 1)}>
      Count: {count()} | Doubled: {doubled()}
    </button>
  );
}

Benefícios da Padronização

Por que incluir Signals na linguagem em vez de deixar como está?

Interoperabilidade Entre Frameworks

Atualmente, signals de diferentes frameworks são incompatíveis:

Problema atual:

  • Um signal do Angular não funciona no Vue
  • Componentes de diferentes frameworks não compartilham estado
  • Bibliotecas precisam escolher qual sistema suportar

Com padronização:

  • Base comum permitiria interoperabilidade
  • Bibliotecas poderiam trabalhar com qualquer framework
  • Migração entre frameworks seria mais simples

Redução de Bundle Size

Cada framework carrega sua própria implementação de reatividade:

Tamanho atual de implementações:

  • Angular Signals: ~8KB
  • Vue Reactivity: ~12KB
  • SolidJS: ~7KB
  • Preact Signals: ~3KB

Com implementação nativa:

  • Zero KB adicional (parte do runtime)
  • Otimizações de engine possíveis
  • Performance potencialmente melhor

Modelo Mental Unificado

Desenvolvedores aprendem o conceito uma vez:

Atualmente:

  • Cada framework tem nomenclatura diferente
  • APIs variam (get/set vs .value vs função)
  • Comportamentos sutilmente diferentes

Com padrão:

  • Um conceito, uma API
  • Documentação centralizada
  • Transferência de conhecimento simplificada

Desafios e Controvérsias

A proposta não é unanimidade. Existem debates importantes na comunidade.

O Debate React

O React não usa Signals internamente:

Posição do time React:

  • Preferem modelo de imutabilidade
  • Signals podem encorajar mutação
  • Complexidade adicional para casos de uso do React

Contraargumento:

  • React poderia implementar layer sobre Signals
  • Não é obrigatório usar, apenas disponível
  • Outros patterns também existem (classes, closures)

Complexidade da Especificação

Alguns argumentam que Signals são muito opinados para a linguagem:

Preocupações:

  • JavaScript nunca teve primitivos de UI antes
  • Pode encorajar patterns específicos
  • Implementação de engine é complexa

Resposta dos autores:

  • Signals são úteis além de UI (observables, data sync)
  • Proposta é minimal e extensível
  • Já existem primitivos complexos (Proxy, WeakMap)

Performance Em Casos Extremos

Sistemas reativos podem ter overhead:

Potenciais problemas:

  • Tracking de dependências tem custo
  • Muitos signals = muita memória
  • Cascatas de atualizações

Mitigações propostas:

  • Lazy evaluation por padrão
  • Batching de notificações
  • Weak references para cleanup

Exemplo Prático: Implementando Um Formulário

Veja como Signals nativos poderiam funcionar em um cenário real:

// Com a proposta, seria algo assim:
const form = {
  email: new Signal.State(''),
  password: new Signal.State(''),
  confirmPassword: new Signal.State('')
};

// Validações derivadas
const emailValid = new Signal.Computed(() => {
  const email = form.email.get();
  return email.includes('@') && email.includes('.');
});

const passwordsMatch = new Signal.Computed(() => {
  return form.password.get() === form.confirmPassword.get();
});

const formValid = new Signal.Computed(() => {
  return emailValid.get() &&
         form.password.get().length >= 8 &&
         passwordsMatch.get();
});

// Uso em framework fictício
function renderForm() {
  return html`
    <form>
      <input
        type="email"
        value=${form.email.get()}
        oninput=${e => form.email.set(e.target.value)}
        class=${emailValid.get() ? 'valid' : 'invalid'}
      />

      <input
        type="password"
        value=${form.password.get()}
        oninput=${e => form.password.set(e.target.value)}
      />

      <input
        type="password"
        value=${form.confirmPassword.get()}
        oninput=${e => form.confirmPassword.set(e.target.value)}
        class=${passwordsMatch.get() ? '' : 'error'}
      />

      <button disabled=${!formValid.get()}>
        Submit
      </button>
    </form>
  `;
}

Timeline e Status Atual

Onde a proposta está no processo do TC39.

Estágio Atual

Status: Stage 1 (Proposal)

Isso significa:

  • O comitê concorda que o problema vale resolver
  • A proposta está sendo ativamente desenvolvida
  • Ainda há mudanças significativas possíveis
  • Não está pronta para implementação

Próximos Passos

Para Stage 2:

  • Especificação inicial completa
  • Semântica bem definida
  • Exemplo de implementação

Para Stage 3:

  • Implementação em pelo menos um engine
  • Feedback de uso real
  • Especificação estável

Para Stage 4:

  • Múltiplas implementações
  • Testes de conformidade
  • Aprovação final

Previsão Conservadora

Timeline estimada:

  • 2026: Refinamento da proposta
  • 2027: Possível Stage 2/3
  • 2028+: Implementações em browsers
  • 2029+: Uso em produção seguro

Como Se Preparar

Mesmo que a proposta demore, você pode se preparar.

Aprenda Signals Agora

Use uma das implementações existentes:

// Preact Signals - muito similar à proposta
import { signal, computed, effect } from '@preact/signals';

const count = signal(0);
const doubled = computed(() => count.value * 2);

effect(() => {
  console.log(doubled.value);
});

Entenda Os Fundamentos

Conceitos que transferem para qualquer implementação:

  • Dependência tracking automático
  • Lazy vs eager evaluation
  • Batching e scheduling
  • Cleanup e lifecycle

Reflexão Final

A proposta de Signals no TC39 representa uma possível evolução fundamental do JavaScript para aplicações reativas. Unificar como frameworks lidam com reatividade poderia simplificar o ecossistema e melhorar a interoperabilidade.

Os pontos-chave:

  • Signals são primitivos de reatividade já provados em frameworks
  • A proposta TC39 busca padronização, não substituição de frameworks
  • Benefícios incluem interoperabilidade e redução de código
  • Timeline é longa - provavelmente 2028+ para uso em produção
  • Aprender signals agora prepara você para o futuro

Independente de quando (ou se) a proposta for aprovada, entender reatividade baseada em signals já é valioso hoje. Angular, Vue, Solid e outros frameworks já adotaram o pattern, e a tendência continua crescendo.

Se você quer explorar mais sobre as novidades do JavaScript moderno, recomendo que dê uma olhada em outro artigo: ES2026: As Novas Features Que Vão Mudar Seu Código onde você vai descobrir outras propostas importantes em andamento.

Bora pra cima! 🦅

Comentários (0)

Esse artigo ainda não possui comentários 😢. Seja o primeiro! 🚀🦅

Adicionar comentário