Signals no React

react
solid
javascript
há cerca de 1 mês

Solid faz um tempo que chegou com a bomba toda, revolucionando os hooks do React com Signals

// No React
function Component() {
  const [first, setFirst] = useState("JSON");
  const [last, setLast] = useState("Bourne");
 
  useEffect(() => {
    console.log(`${first} ${last}`);
  }, [first, last]);
  // ...
}
 
// No Solid
const [first, setFirst] = createSignal("JSON");
const [last, setLast] = createSignal("Bourne");
 
createEffect(() => console.log(`${first()} ${last()}`));

Duas diferenças gritantes:

  • Array de dependências não existem
  • State não é vinculado ao render

Svelte também queria isso, então Svelte 5 está atualizando pra usar "runes", bem no estilo do Solid

let first = $state("JSON")
let last = $state("Bourne")
 
$effect(() => {
  console.log(`${first} ${last}`)
})

Fizeram uma proposta para que signals fossem incluídos no js. Essa é uma proposta bem nova, que está looonge de acontecer, se acontecer. Mas independente, existe uma vontade aí.

A proposta adiciona um namespace global Signal com uma API expondo tudo que é necessário:

declare namespace Signal {
  // state com get/set
  class State<T> implements Signal<T> {
    constructor(initialState: T)
    get(): T;
    set(t: T): void;
  }
 
  // valor computado pelos states
  class Computed<T> implements Signal<T> {
    constructor(cb: (this: Computed<T>) => T);
    get(): T;
  }
 
  // isso aqui ficaria escondido,
  // só sendo usado por bibliotecas e frameworks
  namespace subtle {
    class Watcher {
      constructor(notify: (this: Watcher) => void);
      watch(...s: Signal[])
      unwatch(...s: Signal[])
    }
  }
}
interface Signal<T> {
  get(): T;
}

A ideia é oferecer isso para outras ferramentas usarem internamente, dessa forma temos como melhorar performance de uma forma que eles não tem como, além de possibilitar uma camada de padronização entre as diferentes ferramentas.

React é radicalmente diferente, ele não renderiza componentes da mesma forma que outros frameworks.

Outros renderizam somente uma vez, fazendo uma ligação direta entre state-UI para UI atualizar. Já React rerenderiza cada componente toda vez que o state é atualizado, recomputando toda a árvore de componentes. Por conta disso que React não implementa signals, é uma ferramenta que não se adequa a esse padrão de renderização.

Dito isso, fiz uns experimentos para criar componentes que respondem a signals e effects

const count = new Signal.State(0)
function IncrementButton() {
  // um hook para sinalizar (entendeu?!) o render do React
  useStandaloneRender(count);
 
  // um effect que não precisa declarar dependências
  useSignalEffect(() => {
    console.log(`Count: ${count.get()}`)
  })
 
  return (
    <Button
      onClick={() => count.set(count.get() + 1)}
    >
      Count {count.get()}
    </Button>
  )
}

O código está disponível no meu github, se quiser ver: