What Are Signals? A Deep Dive into Fine‑Grained Reactive State Management

This article explains the concept of Signals as a fast, fine‑grained reactive state primitive, compares its usage and performance with hooks, SolidJS, and Vue, and provides hands‑on implementations and integration tips for modern frontend frameworks.

Alipay Experience Technology
Alipay Experience Technology
Alipay Experience Technology
What Are Signals? A Deep Dive into Fine‑Grained Reactive State Management

What are Signals?

We start with an article by Preact author Jason Miller.

Signal illustration
Signal illustration

Preact introduced Signals, providing fast reactive state primitives (or atoms). Signals have the following characteristics:

They feel like using raw data structures.

They automatically update when values change.

They update the DOM directly (i.e., no VDOM).

They have no dependency array.

Below is a basic usage example of Signals:

import { signal } from "@preact/signals";

const count = signal(0);

function Counter() {
  const value = count.value;
  const increment = () => {
    count.value++;
  };
  return (
    <div>
      <p>Count: {value}</p>
      <button onClick={increment}>click me</button>
    </div>
  );
}

The API is very similar to SolidJS's createSignal, and the .value accessor resembles Vue's Ref. Signals maintain performance even as applications grow, offering fine‑grained state management without memorization tricks. They bypass passing data through the component tree and update referenced components directly, reducing mental overhead and ensuring optimal performance.

Signal concept diagram
Signal concept diagram

Below is a performance comparison between hook‑based state (as atoms) and Signals:

Performance chart
Performance chart

Signals achieve this performance thanks to:

Lazy evaluation – only used values are tracked and updated.

Optimal update strategy.

Optimal dependency tracking – no need to specify dependencies.

Direct state access without selectors or extra hooks.

Signals can exist outside components, unlike hook‑based state.

Can Signals serve both Preact and other frameworks? The goals of Signals show that they can be framework‑agnostic.

Framework agnostic illustration
Framework agnostic illustration

Currently, Signals can be used standalone, without depending on a UI framework. Example sandbox: https://codesandbox.io/s/tender-burnell-7c7g9n?file=/src/index.js

Other Frameworks

Switching to SolidJS, here is a reactive data example:

import { createSignal, onCleanup } from "solid-js";
import { render } from "solid-js/web";

const App = () => {
  const [count, setCount] = createSignal(0);
  const timer = setInterval(() => setCount(count() + 1), 1000);
  onCleanup(() => clearInterval(timer));
  return <div>{count()}</div>;
};

render(() => <App />, document.getElementById("app"));

SolidJS also uses Signals as the foundation; createSignal can be used inside or outside components, similar to Preact. It can serve as local state or global state. The similarities include:

Fine‑grained reactive updates.

No need to define dependencies.

Lazy value retrieval.

SolidJS's reactivity is comparable to Mobx and Vue, but it updates the DOM directly without a VDOM, resulting in strong performance.

SolidJS performance chart
SolidJS performance chart

Three Elements of Reactive State Management

Reactive state management diagram
Reactive state management diagram

Signal

A Signal is a reactive data unit; similar concepts are called Observables, Atoms, or Refs in other libraries.

const [count, setCount] = createSignal(0);
console.log(count()); // 0
setCount(5);
console.log(count()); // 5

Some APIs use .value instead of a function call, which can be implemented with getters, setters, or proxies.

Reactions

Reactions (often called Effects) run side‑effects when data changes.

const [count, setCount] = createSignal(0);
createEffect(() => console.log("The count is", count()));
setCount(5);

Derivations

Derivations (e.g., computed, memo) derive new values from signals and cache results.

const [firstName, setFirstName] = createSignal("John");
const [lastName, setLastName] = createSignal("Smith");
const fullName = createMemo(() => `${firstName()} ${lastName()}`);
console.log(fullName);

Reactive Characteristics

Reactive data management stores links between nodes; when a node updates, the system checks and updates linked nodes, removing stale dependencies.

Reactive graph illustration
Reactive graph illustration

Example demonstrating fine‑grained updates:

const [firstName, setFirstName] = createSignal("a");
const [lastName, setLastName] = createSignal("b");
const [showFullName, setShowFullName] = createSignal(true);

const displayName = createMemo(() => {
  if (!showFullName()) return firstName();
  return `${firstName()} ${lastName()}`;
});

createEffect(() => console.log("Name:", displayName()));
// a b
setShowFullName(false); // a
setLastName("c"); // no change
setShowFullName(true); // a c

Running the example: https://codesandbox.io/s/tender-burnell-7c7g9n?file=/src/index.js

Manual Implementation

Reactive state management relies on the observer pattern: when a Signal updates, Reactions subscribed to it are triggered.

Signals

Basic implementation of a signal:

const createSignal = (value) => {
  const setter = (newValue) => value = newValue;
  return [() => value, setter];
};

const [name, setName] = createSignal('a');
console.log(name());
setName('b');
console.log(name());
Signal output
Signal output

Reactions

Implementation of a reaction (effect) with subscription handling:

const context = [];

const createEffect = (fn) => {
  const execute = () => {
    running.deps.clear();
    context.push(running);
    try { fn(); } finally { context.pop(running); }
  };
  const running = { execute, deps: new Set() };
  execute();
};

Derivations

Derivation (memo) implementation:

const createMemo = (fn) => {
  const [memo, setMemo] = createSignal();
  createEffect(() => setMemo(fn()));
  return memo;
};

Full demo: https://codesandbox.io/s/elastic-blackwell-br79m2?file=/src/index.js

Back to React

React offers many state‑management options; see the referenced article for a comparison.

React state management comparison
React state management comparison

Valtio aligns closely with Signals in React, providing:

No dependency array.

External store without component coupling.

However, React still relies on VDOM updates, unlike SolidJS's direct DOM updates. Future React improvements such as React Forget or useEvent may address this.

Conclusion

This article does not aim to guide state‑management selection or compare pros and cons; it simply introduces concepts and methods of reactive state management for reference.

References

https://preactjs.com/blog/introducing-signals

https://preactjs.com/guide/v10/signals/

https://dev.to/ryansolid/a-hands-on-introduction-to-fine-grained-reactivity-3ndf

https://indepth.dev/posts/1289/solidjs-reactivity-to-rendering

https://cn.vuejs.org/guide/extras/reactivity-in-depth.html

https://my5353.com/gCocL

https://mp.weixin.qq.com/s/26_yYH5fbDyMTEKOMcNxtA

frontendsolidjsPreactsignalsreactive state
Alipay Experience Technology
Written by

Alipay Experience Technology

Exploring ultimate user experience and best engineering practices

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.