Did Signals Just Land in React?

Last week, Daishi Kato (creator of Waku) released use-signals, an experimental React hook for TC39 signals, which aims to demonstrate how Signals can work in React.

Screenshot of Tweet from Daishi announcing use-signals

What Are Signals?

Signals have been in development for around 10 years and are used by JavaScript frameworks such as Angular, Vue, SolidJS, Qwik and Preact, (amongst others), and are used to manage client-side state. Signals are variables that auto-track where they’ve been used. When a change to a Signal occurs, its value is invalidated — which causes the UI state to update / re-render.

For example, here’s a Signal named counter which holds a value of 0.

Signals are fundamentally different from React’s useState. useState is a hook provided by React for managing state within functional components and allows you to declare a state variable and a function to update that variable.

Signals are event listeners, or observers for handling asynchronous events or changes in data that occur outside of the component’s immediate control. As such, you’ll notice that there’s no “setter” function defined in the Signal declaration. Whereas, with React, there is. For example here’s how the above Signal would be declared in React.

The reason Signals are an interesting concept is that React’s model of “top down” means that whenever a state value changes, all descendants of the component tree are re-rendered and any relevant changes to the UI are made — thus keeping the DOM / UI in sync with the application state.

Using a Signal to manage state, however, allows for more fine-grained control over what parts of a UI are “re-rendered.” That’s not to say that Signals are any more performant than the React approach, but they are fundamentally different from one another.

Signals Under the Hood

As shown above, a Signal can be declared using the new constructor E.g.:

Then, to “get” the value of a Signal you can use the .get() method; and to “set” or update a Signal, you can use the .set() method. E.g.:

What Are Signals in React?

Contrary to my above notes relating to how Signals work, that’s not how they work in React. To bypass React’s diffing would go against React’s core principles of declarative programming. So Signals in React will still use the VDOM and will still cause re-renders the same way a change to useState would.

So what’s the point of Signals in React? This was my first thought too, and Daishi has even written about it: Why You Don’t Need Signals in React. So let’s dive a little deeper into the world of Signals.

TC39 Proposal

If the TC39 proposal is successful and Signals become natively available in JavaScript, we’ll be able to use Signals outside of frameworks. More than that, framework authors should — in theory — be able to implement Signals in a standardized way. Any advancements to Signals should benefit all frameworks that have implemented the standardized approach. Currently, many of the frameworks that use Signals have all approached it in slightly different ways.

On April 11, Rob Eisenberg announced that the TC39 proposal has advanced to stage 1. This officially means the proposal is now under ECMAScript consideration; and whilst there is a lot of work still to do, the proposal appears to be moving in the right direction.

Screenshot of Tweet from Rob announcing Signal progressing to stage 1

If you’re curious to know more, here’s a link to the slide deck: Developing Signals (For Stage 1). Also, here’s a summary of the meeting notes: Updates from the 101th TC39 meeting.

The TC39 proposal also mentions the importance of developing the API in such a way as to allow for framework-specific requirements to be implemented. This is somewhat the case with use-signals because it uses the (proposed) Signals API but still honors React’s core principles.

Whether or not the React team would opt to adopt Signals is a different matter, but use-signals goes some way towards showing that Signals would work in React.

Signal Utils Proposal

Signals at the moment only support primitives, but there are additional signal-utils proposals in progress that could bring Object and Array to Signals. For example:

Object

Array

Signals in React Example

With all that said, let’s have a look at how use-signals would look in React. The React code I’ll be showing works in the context of Waku, which is server-side rendered by default but does support the 'use client' directive for client-only components.

Unlike useState, with Signals a new Signal declaration is declared outside of the component. In the below snippet, it’s the const named counter. This is used to store the initial value and can be passed on to the useSignal hook.

The count const exposes both the .set() and .get() methods, which can be used inside the event handler function, handleInc.

Changes to the Signal value will cause the DOM to update and the new count value to be displayed in the UI.

What’s interesting here is that in the returned Jsx you don’t need to use .get() to access and display the value within the HTML <div /> element. Instead, the count value can be accessed directly. This is slightly different to Qwik’s implementation, which would require you to access the value from the counter, E.g. counter.value.

useState Component

And just for good measure, here’s the same logic but written using React’s useState hook. As you can see, there’s little difference between the two.

Mixed Signals

So there you go, Signals in React — they are possible, and while it might take some time before Signals make it into native JavaScript, I do really love the thirst this thriving community has for developing new ideas. If you’d like to get involved, head over to the GitHub repository: dai-shi/use-signals.

Group Created with Sketch.

 

 

 

 

Top