Voltar para o Blog

Signals no JavaScript: O Padrao Nativo de Reatividade Que Esta Chegando

Olá HaWkers, uma das propostas mais empolgantes para o futuro do JavaScript está ganhando força. Os Signals, um padrão de reatividade já adotado por frameworks como Angular, Vue, Solid e Svelte, podem em breve se tornar parte nativa da linguagem JavaScript.

Você está pronto para uma mudança que pode unificar como todos os frameworks lidam com estado reativo?

O Que São Signals

Signals são primitivas de reatividade que permitem criar valores observáveis que automaticamente notificam dependentes quando mudam. Diferente de soluções como Redux ou Context API, Signals são granulares e eficientes por design.

Conceito Básico

// Proposta atual de Signals nativos
// (sintaxe pode mudar até a aprovação final)

// Criando um signal
const count = new Signal.State(0);

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

// Atualizando o valor
count.set(1);

// Valores computados que reagem automaticamente
const doubled = new Signal.Computed(() => count.get() * 2);

console.log(doubled.get()); // 2

// Quando count muda, doubled atualiza automaticamente
count.set(5);
console.log(doubled.get()); // 10

Contexto importante: A proposta está sendo desenvolvida pelo TC39 (comitê que define o ECMAScript) com contribuições de engenheiros do Angular, Vue, Solid e outros frameworks.

Por Que Signals São Importantes

A reatividade é fundamental para interfaces modernas, mas cada framework implementa sua própria solução. Isso causa fragmentação e dificulta a interoperabilidade.

O Problema Atual

Cada framework tem sua abordagem:

  • React: Virtual DOM + reconciliação
  • Vue: Proxies + sistema de reatividade próprio
  • Angular: Zone.js + signals (recente)
  • Solid: Signals + compilação
  • Svelte: Compilação + reatividade em tempo de build

Consequências:

  • Código não portável entre frameworks
  • Bibliotecas precisam de wrappers para cada framework
  • Desenvolvedores aprendem sistemas diferentes
  • Performance varia muito entre implementações

A Solução: Signals Nativos

// Com signals nativos, bibliotecas podem ser agnósticas

// Uma biblioteca de estado que funciona em qualquer lugar
class Counter {
  #count = new Signal.State(0);

  get count() {
    return this.#count.get();
  }

  increment() {
    this.#count.set(this.#count.get() + 1);
  }

  decrement() {
    this.#count.set(this.#count.get() - 1);
  }
}

// Funciona com React, Vue, Angular, Solid, Svelte...
const counter = new Counter();

Como Signals Funcionam

Signals são baseados em um grafo de dependências que rastreia automaticamente quais valores dependem de quais.

Arquitetura Básica

// Signal.State: valores que podem ser lidos e escritos
const firstName = new Signal.State("John");
const lastName = new Signal.State("Doe");

// Signal.Computed: valores derivados (somente leitura)
const fullName = new Signal.Computed(() => {
  // Dependências são rastreadas automaticamente
  return `${firstName.get()} ${lastName.get()}`;
});

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

// Apenas firstName é atualizado
firstName.set("Jane");

// fullName recalcula automaticamente apenas o necessário
console.log(fullName.get()); // "Jane Doe"

Efeitos e Reações

// Signal.subtle.Watcher: observar mudanças
const watcher = new Signal.subtle.Watcher(() => {
  console.log("Algum signal monitorado mudou!");
});

// Adicionar signals para monitorar
watcher.watch(firstName);
watcher.watch(lastName);

// Quando qualquer um mudar, o callback é chamado
firstName.set("Alice"); // Log: "Algum signal monitorado mudou!"

Batching Automático

// Múltiplas atualizações são agrupadas eficientemente
const items = new Signal.State([]);
const filter = new Signal.State("");

const filteredItems = new Signal.Computed(() => {
  const f = filter.get();
  return items.get().filter(item => item.includes(f));
});

// Mesmo com múltiplas atualizações...
items.set(["apple", "banana", "cherry"]);
filter.set("a");

// ...o computed só recalcula uma vez
console.log(filteredItems.get()); // ["apple", "banana"]

Comparação Com Soluções Atuais

Veja como Signals nativos se comparam com as abordagens atuais.

React useState vs Signals

// React: useState
function Counter() {
  const [count, setCount] = useState(0);
  const doubled = count * 2; // Recalcula em todo render

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

// Com Signals (conceitual)
function Counter() {
  const count = useSignal(0);
  const doubled = useComputed(() => count.value * 2);

  return (
    <div>
      <p>Count: {count.value}</p>
      <p>Doubled: {doubled.value}</p>
      <button onClick={() => count.value++}>+</button>
    </div>
  );
}

Vue Reactivity vs Signals

// Vue: ref e computed
import { ref, computed } from 'vue';

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

// Signals nativos (similar, mas padronizado)
const count = new Signal.State(0);
const doubled = new Signal.Computed(() => count.get() * 2);

Solid Signals (já usa o padrão)

// Solid: signals (inspiração principal)
import { createSignal, createMemo } from 'solid-js';

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

// Signals nativos (muito similar)
const count = new Signal.State(0);
const doubled = new Signal.Computed(() => count.get() * 2);

Benefícios Para Desenvolvedores

A adoção de Signals nativos trará vários benefícios.

Performance Granular

// Signals atualizam apenas o que precisa atualizar
const todos = new Signal.State([
  { id: 1, text: "Learn Signals", done: false },
  { id: 2, text: "Build app", done: false },
]);

const completedCount = new Signal.Computed(() => {
  return todos.get().filter(t => t.done).length;
});

const pendingCount = new Signal.Computed(() => {
  return todos.get().filter(t => !t.done).length;
});

// Se apenas 'done' de um item mudar,
// apenas os computeds relevantes recalculam

Código Mais Simples

// Sem signals: gerenciamento manual de estado
class Store {
  constructor() {
    this.state = { count: 0 };
    this.listeners = [];
  }

  subscribe(listener) {
    this.listeners.push(listener);
    return () => {
      this.listeners = this.listeners.filter(l => l !== listener);
    };
  }

  increment() {
    this.state.count++;
    this.listeners.forEach(l => l(this.state));
  }
}

// Com signals: reatividade automática
class Store {
  count = new Signal.State(0);

  increment() {
    this.count.set(this.count.get() + 1);
  }
}

Debugging Melhor

// Signals permitem rastrear dependências facilmente
const a = new Signal.State(1);
const b = new Signal.State(2);
const c = new Signal.Computed(() => a.get() + b.get());
const d = new Signal.Computed(() => c.get() * 2);

// DevTools podem mostrar:
// d depende de c
// c depende de a e b
// Quando a muda, c e d são recalculados

Estado Atual da Proposta

A proposta de Signals está em desenvolvimento ativo no TC39.

Timeline Esperado

2024:

  • Stage 1: Proposta inicial aceita
  • Discussões sobre API e semântica

2025:

  • Stage 2: Especificação em draft
  • Implementações experimentais em engines
  • Polyfills disponíveis

2026-2027 (estimativa):

  • Stage 3: Candidato a implementação
  • Browsers começam implementações

2027+ (estimativa):

  • Stage 4: Aprovação final
  • Disponível em todos os browsers modernos

Participantes Principais

Organização Contribuição
Angular Team Co-autores da proposta
Vue.js Core Consultoria técnica
Solid.js Inspiração do design
Bloomberg Patrocínio e engenharia
TC39 Processo de padronização

Como Se Preparar

Você pode começar a entender Signals hoje.

Aprenda Solid.js

// Solid tem a API mais próxima da proposta
import { createSignal, createEffect } from 'solid-js';

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

createEffect(() => {
  console.log("Count changed:", count());
});

setCount(1); // Log: "Count changed: 1"

Explore Angular Signals

// Angular 16+ usa signals nativamente
import { signal, computed, effect } from '@angular/core';

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

effect(() => {
  console.log(`Count: ${count()}, Doubled: ${doubled()}`);
});

count.set(5); // Log: "Count: 5, Doubled: 10"

Use o Polyfill

// signal-polyfill disponível via npm
import { Signal } from 'signal-polyfill';

const count = new Signal.State(0);
const doubled = new Signal.Computed(() => count.get() * 2);

Conclusão

Signals nativos no JavaScript representam uma mudança fundamental em como construímos aplicações reativas. Ao padronizar a reatividade na linguagem, ganhamos interoperabilidade entre frameworks, performance consistente e código mais simples.

Embora a proposta ainda esteja em desenvolvimento, os conceitos já estão disponíveis em frameworks como Solid e Angular. Aprender sobre Signals hoje é investir no futuro do desenvolvimento web.

Se você quer entender mais sobre as tendências que estão moldando o JavaScript moderno, recomendo dar uma olhada em nosso artigo sobre VoidZero e Vite Plus onde discutimos a nova geração de ferramentas JavaScript.

Bora pra cima! 🦅

Comentários (0)

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

Adicionar comentário