Signals em JavaScript: O Futuro da Reatividade Que Pode Mudar a Web
Ola HaWkers, se voce trabalha com frameworks JavaScript modernos, provavelmente ja ouviu falar de Signals. O que comecou como uma abordagem alternativa ao gerenciamento de estado esta se tornando um consenso entre Angular, Vue, Solid, Svelte e potencialmente entrando na especificacao oficial do JavaScript.
Vamos entender o que sao Signals, por que estao conquistando a comunidade e como eles podem mudar a forma como escrevemos aplicacoes web.
O Que Sao Signals
Conceito Fundamental
Signals sao primitivas reativas que armazenam um valor e notificam automaticamente seus dependentes quando esse valor muda. Diferente do modelo tradicional de re-renderizacao completa, Signals permitem atualizacoes cirurgicas apenas onde necessario.
Anatomia basica de um Signal:
// Criando um signal
const count = signal(0)
// Lendo o valor
console.log(count()) // 0
// Atualizando o valor
count.set(1)
// ou
count.update(n => n + 1)
// Derivando valores (computed)
const doubled = computed(() => count() * 2)
// Reagindo a mudancas (effect)
effect(() => {
console.log(`Count is: ${count()}`)
})Por Que Signals Sao Diferentes
A magia dos Signals esta na reatividade fina (fine-grained reactivity). Compare com abordagens tradicionais:
React (re-render completo):
function Counter() {
const [count, setCount] = useState(0)
const [name, setName] = useState('User')
// Quando count muda, TODO o componente re-renderiza
// Incluindo partes que dependem apenas de name
return (
<div>
<h1>Hello, {name}</h1>
<p>Count: {count}</p>
<button onClick={() => setCount(c => c + 1)}>+</button>
</div>
)
}Com Signals (update cirurgico):
function Counter() {
const count = signal(0)
const name = signal('User')
// Quando count muda, APENAS o texto do count atualiza
// O h1 com name nem e tocado
return (
<div>
<h1>Hello, {name()}</h1>
<p>Count: {count()}</p>
<button onClick={() => count.update(c => c + 1)}>+</button>
</div>
)
}
A Convergencia dos Frameworks
Quem Esta Usando Signals
Em 2026, praticamente todos os frameworks JavaScript adotaram ou estao adotando Signals.
Status por framework:
| Framework | Status Signals | Desde |
|---|---|---|
| Solid.js | Nativo (pioneiro) | 2021 |
| Angular | Signals API | 2023 |
| Vue 3.6 | Alien Signals | 2026 |
| Svelte 5 | Runes ($state) | 2024 |
| Preact | @preact/signals | 2022 |
| Qwik | Signals nativos | 2022 |
| React | Em discussao | - |
A Proposta TC39
O mais interessante e que Signals podem se tornar parte do JavaScript nativo. Ha uma proposta ativa no TC39 para adicionar Signals a especificacao.
Status da proposta:
TC39 Proposal: Signals
Stage: 1 (Proposal)
Champions: Daniel Ehrenberg, Rob Riggs
Objetivo: Criar uma primitiva de reatividade padronizada
que funcione como base para todos os frameworksProposta de API nativa:
// Possivel API nativa (especulativa)
const count = Signal.state(0)
const doubled = Signal.computed(() => count.get() * 2)
Signal.effect(() => {
console.log(doubled.get())
})
count.set(5) // Logs: 10
Como Signals Funcionam Por Dentro
O Sistema de Dependencias
Signals usam um grafo de dependencias que e construido automaticamente durante a execucao.
// Internamente, algo assim acontece:
class Signal {
#value
#subscribers = new Set()
constructor(initialValue) {
this.#value = initialValue
}
get() {
// Se estamos dentro de um effect/computed,
// registra essa dependencia
if (currentTracking) {
this.#subscribers.add(currentTracking)
}
return this.#value
}
set(newValue) {
if (this.#value !== newValue) {
this.#value = newValue
// Notifica todos os subscribers
this.#subscribers.forEach(sub => sub.notify())
}
}
}Tracking Automatico
O tracking de dependencias acontece automaticamente quando voce le um signal dentro de um contexto reativo.
const firstName = signal('John')
const lastName = signal('Doe')
const age = signal(30)
// Este computed depende APENAS de firstName e lastName
// age nao e uma dependencia porque nao foi lido
const fullName = computed(() => {
return `${firstName()} ${lastName()}`
})
// Mudar age NAO recalcula fullName
age.set(31) // fullName nao reage
// Mudar firstName recalcula fullName
firstName.set('Jane') // fullName recalculaPush vs Pull
Signals usam um modelo hibrido de push-pull:
// PUSH: Quando um signal muda, ele "empurra" notificacao
// para seus dependentes diretos
const a = signal(1)
const b = computed(() => a() * 2) // b depende de a
const c = computed(() => b() + 10) // c depende de b
a.set(2) // Push: a notifica b, b notifica c
// PULL: Valores so sao recalculados quando lidos
// (lazy evaluation)
console.log(c()) // Pull: c calcula b, b calcula a
Implementacoes Praticas
Signals no Angular
Angular introduziu Signals como alternativa ao RxJS para casos simples.
import { signal, computed, effect } from '@angular/core'
@Component({
selector: 'app-counter',
template: `
<h1>Count: {{ count() }}</h1>
<h2>Doubled: {{ doubled() }}</h2>
<button (click)="increment()">+</button>
`
})
export class CounterComponent {
count = signal(0)
doubled = computed(() => this.count() * 2)
constructor() {
effect(() => {
console.log(`Count changed to: ${this.count()}`)
})
}
increment() {
this.count.update(n => n + 1)
}
}Signals no Solid.js
Solid foi o pioneiro em Signals modernos.
import { createSignal, createEffect, createMemo } from 'solid-js'
function Counter() {
const [count, setCount] = createSignal(0)
const doubled = createMemo(() => count() * 2)
createEffect(() => {
console.log(`Count is ${count()}`)
})
return (
<div>
<p>Count: {count()}</p>
<p>Doubled: {doubled()}</p>
<button onClick={() => setCount(c => c + 1)}>+</button>
</div>
)
}Signals no Vue 3.6
Vue esta introduzindo Signals atraves do Vapor Mode.
<script setup>
import { signal, computed, effect } from 'vue/vapor'
const count = signal(0)
const doubled = computed(() => count() * 2)
effect(() => {
console.log(`Count: ${count()}`)
})
function increment() {
count.set(count() + 1)
}
</script>
<template>
<div>
<p>Count: {{ count() }}</p>
<p>Doubled: {{ doubled() }}</p>
<button @click="increment">+</button>
</div>
</template>
Padroes Avancados com Signals
Signals Derivados Complexos
// Multiplas dependencias
const items = signal([
{ name: 'Apple', price: 1.5, qty: 3 },
{ name: 'Banana', price: 0.75, qty: 5 },
])
const taxRate = signal(0.1)
// Computed que depende de items e taxRate
const total = computed(() => {
const subtotal = items().reduce(
(sum, item) => sum + item.price * item.qty,
0
)
return subtotal * (1 + taxRate())
})
// Atualizar qualquer dependencia recalcula total
items.update(i => [...i, { name: 'Orange', price: 2, qty: 2 }])Effects com Cleanup
// Effects podem retornar funcao de cleanup
const userId = signal(1)
effect(() => {
const id = userId()
// Setup: conectar WebSocket
const ws = new WebSocket(`/user/${id}/updates`)
ws.onmessage = (event) => {
console.log('Update:', event.data)
}
// Cleanup: desconectar quando userId mudar
return () => {
ws.close()
}
})
userId.set(2) // Fecha WS antigo, abre novoBatching de Updates
// Multiplos updates em sequencia
const a = signal(1)
const b = signal(2)
const c = signal(3)
const sum = computed(() => a() + b() + c())
effect(() => {
console.log('Sum:', sum())
})
// Sem batching: 3 recalculos
a.set(10)
b.set(20)
c.set(30)
// Com batching: 1 recalculo
batch(() => {
a.set(10)
b.set(20)
c.set(30)
})
Signals vs Outras Abordagens
Comparacao com Redux
// Redux: Muito boilerplate
const store = createStore(reducer)
const mapStateToProps = state => ({ count: state.count })
const mapDispatchToProps = { increment }
connect(mapStateToProps, mapDispatchToProps)(Component)
// Signals: Direto ao ponto
const count = signal(0)
const increment = () => count.update(n => n + 1)Comparacao com RxJS
// RxJS: Poderoso mas complexo
const count$ = new BehaviorSubject(0)
const doubled$ = count$.pipe(map(n => n * 2))
doubled$.subscribe(console.log)
count$.next(5)
// Signals: Mais simples para casos comuns
const count = signal(0)
const doubled = computed(() => count() * 2)
effect(() => console.log(doubled()))
count.set(5)Quando Usar Cada Abordagem
| Cenario | Melhor Opcao |
|---|---|
| Estado local de componente | Signals |
| Streams de dados complexos | RxJS |
| Estado global simples | Signals |
| Estado global complexo | Redux/Zustand |
| Dados assincronos | Signals + async |
| Event sourcing | RxJS |
Performance de Signals
Benchmarks
Signals oferecem performance superior em cenarios de updates frequentes.
Teste: 10.000 updates em cascata:
| Abordagem | Tempo | Memory |
|---|---|---|
| Signals (Solid) | 12ms | 2.1MB |
| Signals (Preact) | 15ms | 2.4MB |
| Vue 3 (ref) | 45ms | 4.2MB |
| React (useState) | 180ms | 8.5MB |
| MobX | 28ms | 3.8MB |
Por que Signals sao rapidos:
- Sem VDOM diff - Updates diretos ao DOM
- Tracking preciso - So recalcula o necessario
- Lazy evaluation - Computa apenas quando lido
- Batching automatico - Agrupa updates
Otimizacoes Comuns
// 1. Evite computeds desnecessarios
// Ruim
const doubled = computed(() => count() * 2)
const quadrupled = computed(() => doubled() * 2) // Encadeamento
// Melhor
const quadrupled = computed(() => count() * 4)
// 2. Use untrack para leituras nao reativas
import { untrack } from 'solid-js'
effect(() => {
const currentCount = count()
// Esta leitura NAO cria dependencia
const config = untrack(() => configSignal())
console.log(currentCount, config)
})
// 3. Memoize computacoes pesadas
const expensiveResult = computed(() => {
return items().map(item => heavyComputation(item))
}, { equals: deepEquals })
O Futuro dos Signals
Padronizacao TC39
Se a proposta TC39 for aprovada, teremos Signals nativos no JavaScript.
Timeline estimado:
- 2024: Stage 1 (atual)
- 2025: Stage 2 (Draft)
- 2026-2027: Stage 3 (Candidate)
- 2028+: Stage 4 (Finished)
Beneficios da padronizacao:
- Interoperabilidade entre frameworks
- Performance otimizada pelos engines
- Menos dependencias externas
- API consistente
Impacto nos Frameworks
Com Signals nativos, frameworks poderiam:
// Hoje: Cada framework tem sua implementacao
import { signal } from 'solid-js' // Solid
import { ref } from 'vue' // Vue
import { signal } from '@angular/core' // Angular
// Futuro: Todos usam a mesma base
import { Signal } from 'std:signals' // NativoReact e Signals
React ainda nao adotou Signals oficialmente, mas ha discussoes.
Perspectivas:
- React Forget (compiler) resolve parte do problema
- Signals contradiria o modelo mental do React
- Possivel adocao parcial ou biblioteca oficial
- Comunidade ja usa @preact/signals-react
Conclusao
Signals representam uma evolucao natural no gerenciamento de estado JavaScript. A convergencia dos principais frameworks em torno dessa primitiva, combinada com a proposta TC39, sugere que Signals serao parte fundamental do desenvolvimento web nos proximos anos.
Pontos principais:
- Signals oferecem reatividade fina e performatica
- Angular, Vue, Solid, Svelte ja adotaram
- Proposta TC39 pode tornar Signals nativos
- Performance superior a abordagens tradicionais
- API simples e intuitiva
Recomendacoes:
- Aprenda a API de Signals do seu framework
- Experimente Solid.js para entender Signals puros
- Acompanhe a proposta TC39
- Considere Signals para novos projetos
O futuro da reatividade na web esta sendo definido agora, e Signals estao no centro dessa transformacao.
Para mais sobre evolucao dos frameworks, leia: Vue 3.6 Vapor Mode: A Revolucao de Performance.

