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 atualizadoPor Que Signals Importam
Problemas que Signals resolvem:
Atualizações granulares: Em vez de re-renderizar componentes inteiros, apenas o que depende do valor muda
Sincronização automática: Valores derivados são sempre consistentes com suas fontes
Performance previsível: O sistema sabe exatamente o que precisa atualizar
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()); // 1Signal.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 automaticamenteO 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.

