Signals in SolidJS vs Qwik

prasanth-melethil
5 min readMar 25, 2023

State management is a critical part of an application and often one of the most challenging part. Many libraries come and go frequently in an attempt to solve this issue. However, with the release of React hooks, state management has become much easier.

React useState Problems

One issue with the useState hook in React is that, when the setState() function is called to indicate a change in state, React is unable to determine which part of the page has been altered. As a result, the entire component tree must be re-rendered, resulting in a computationally expensive operation. This causes all child components to re-render, even if they do not directly use the state.

React Component tree updates

With the use of React.Memo, we can prevent the re-render of the components( child 1 and child 3) which are not consuming the state. But even in this case, the component (child 2) which is using the state will be fully re-rendered.

As you can see in the above video, the chldTwo is rendering on parent state change.

Here comes the Signals

With the release of Signal, originally from SolidJS, state management has taken a different direction. The Solid team calls it ‘fine-grained reactivity’. SolidJS follow a reactive programming model where state is managed outside of components and updates to state are automatically propagated to the components that use that state.

Signal is similar to the useState hook used to store the state of an application, but the major difference is that signals can track where the state is being used in the application. This means that signals will be able to update only the components that are actually using the signal, rather than updating the whole component tree as we saw in the example with React’s useState hook. SolidJS achieves this by returning a reference to the state (using signals) instead of just the value (as with useState)

Lets take an example below

import { createSignal } from "solid-js";

function Counter() {
const [count, setCount] = createSignal(0);
console.log('re-render component');
setInterval(() => setCount(count() + 1), 1000);

return <div>{count()}</div>;
}

Here createSignal will return a getter(count) and setter(setCount). By invoking the getter, signal will come to know the place of the application where it is used . If the value changes, this place needs to be re-evaluated. So by calling the getter function we are actually subscribing to the signal.

Signals are event emitters that hold a list of subscriptions. They notify their subscribers whenever their value changes.

In the above example the ‘re-render component’ log will only execute once. This is because unlike react SolidJS does not have a VDOM. Any changes in the value will directly update the DOM. Also unlike react it does not re- render(re execute) the whole component on the state change, because of which all functions defined inside a component will only execute once in the initial rendering phase.

But there is one drawback on this approach of not re-rendering the component on value change, which Dan Abramov mentioned in the below article.

Please go through the above article and comments to get to know more about the drawback. I will just summarise it in here.

When using the “render only once” approach in SolidJS, if there is any content, such as a text node, in the child JSX that needs to change when the parent state (signal) changes, the logic for handling that change should be wrapped in a function that can be called from the JSX code which is kind of an over head for this simple logic. React handles this in much better way.

Example below is taken from the same as that of Dan Abramov.

As you can see above the ‘heading’ depends on the length of videos (signal). To change the heading text on the videos length change, the logic for handling text change is wrapper in heading() function.

QwikJS useSignals to the rescue

Qwik solves the above mentioned problem by re-rendering the whole component that is subscribed to the signal instead of only updating the specific values inside of the JSX which SolidJS does.

Look at the below example

Although the syntax has changed, the underlying mechanism remains unchanged; instead of a getter/setter, a single object with a .value property has been introduced to represent it.

As you can see in the video, “<Child> render” is printed in the console again when the signal value changes(click on increment button), even without being wrapped inside a function like we did in Solid. This is because, unlike Solid, Qwik re-renders the entire component that is subscribed to the signal. Additionally, unlike React, only the subscribed component is re-rendered without the use of useMemo, which is another advantage of using signals.

Look at another example with a small change from the previous example.

As you can se in the above video, the “<Child> render” log is not printed in the console, even though the we incremented the signal value. This is because Qwik is designed to recognise when a signal value is only used inside JSX and not used outside the return JSX value. As a result, it updates the JSX directly, making the process even faster.

Along with that Qwik has also few other benefits such as resumability as in the below docs

Conclusion

I believe that QuickJS is a powerful framework that successfully tackles the problem of Solid JS. It is an impressive tool for creating libraries, and I am really fond of it.

--

--