SolidJS for beginners: a story of virtual DOM and signals
Elise RECEJAC7 min read
Signals are everywhere. After some years of React domination where signals were put aside, they are now back! A lot of rising frameworks use it (like Preact or Qwik), and even Angular integrated it in version 16.
I would like to focus on a less-known framework: SolidJS. It first appeared in 2021 in the StateOfJS.
Solid and the galaxy of frameworks: State Of JS 2022
Disclaimer: you’ll need some basics on React to understand this article.
TL;DR
Cheatsheet React vs SolidJS
Learning by doing: an example
I’ve implemented the same feature in both React and Solid (inspired by a great article on signals in Solid: A Hands-on Introduction to Fine-Grained Reactivity).
The feature is the following:
And I have made two implementations: one using React and the other using SolidJS.
React implementation:
function App() {
console.log("Create component");
const [firstName, setFirstName] = useState("John");
const [lastName, setLastName] = useState("Smith");
const [showFullName, setShowFullName] = useState(true);
const displayName = useMemo(() => {
console.log("Generate display name");
return !showFullName ? firstName : `${firstName} ${lastName}`;
}, [firstName, lastName, showFullName]);
useEffect(() => console.log("My name is", displayName), [displayName]);
return (
<>
<div>{displayName}</div>
<button onClick={() => setShowFullName(true)}>Show Full Name</button>
<button onClick={() => setShowFullName(false)}>
Don't Show Full Name
</button>
<button onClick={() => setLastName("Legend")}>Change Last Name</button>
</>
);
}
Solid implementation:
const App: Component = () => {
console.log("Create component");
const [firstName, setFirstName] = createSignal("John");
const [lastName, setLastName] = createSignal("Smith");
const [showFullName, setShowFullName] = createSignal(true);
const displayName = () => {
console.log("Generate display name");
if (!showFullName()) return firstName();
return `${firstName()} ${lastName()}`;
};
createEffect(() => console.log("My name is", displayName()));
return (
<>
<div>{displayName()}</div>
<button onClick={() => setShowFullName(true)}>Show Full Name</button>
<button onClick={() => setShowFullName(false)}>
Don't Show Full Name
</button>
<button onClick={() => setLastName("Legend")}>Change Last Name</button>
</>
);
};
First thing you should notice: both implementations are quite similar. Indeed, SolidJS is strongly inspired by React syntax and you can see a lot of similarities between both frameworks.
Main similarities between React and Solid
Both frameworks have a connection to the change management system. Both use a central object to store component data and track their changes: state
for React and signal
for Solid. Both use a system to re-execute commands when data changes: use/createEffect
. And both have a system of memoization: use/createMemo
, even if, as you’ll see later, memoization is far less useful in Solid.
So where does SolidJS differ from React?
The main difference between SolidJS and React lies in how the UI is updated to reflect a change in data.
Updates in React
In React, each time a component is mounted or updated, the function render
will be called to send the new HTML to the DOM. It shows that each time an update is made, the full component is re-executed.
React component lifecycle
Example
For example, for my feature:
The first logs correspond to the mounting of the components. Then, I interacted several times:
- Modification of
showFullName
state: the component is re-rendered and theuseEffect
executed sincedisplayName
has changed. - Modification of
lastName
state: the component is re-rendered,displayName
doesn’t change (showFullName
isfalse
), thereforeuseEffect
isn’t re-executed. - Modification of
showFullName
state: the component is re-rendered anduseEffect
is re-executed sincedisplayName
has changed.
Hence, each state modification triggers a re-render of the entire component even if the visual has not changed.
Virtual DOM
To re-render components on state update, React uses a virtual DOM. Indeed, the modification of the DOM is really expensive, hence it’s important to modify as few elements as possible, especially since each time an element of the DOM is modified, each of its children must be modified too.
The virtual DOM is used to calculate the optimal way to update the DOM.
When a state is updated in a component, a capture of the DOM is made at the state before the update, and a process called “diffing” computes the difference before and after the update on this projection of the DOM called “virtual DOM”. This process computes the optimal way to update the real DOM.
Virtual DOM and “diffing”
On its side, SolidJS doesn’t use a virtual DOM.
Updates in SolidJS
SolidJS uses something called “fine-grained reactivity” to update its DOM.
Fine-grained reactivity
The fine-grained reactivity allows Solid to update elements more surgically, recomputing only precise nodes (or functions) using the changed data. It doesn’t need a virtual DOM to update the real DOM efficiently. Solid is even faster than React (you can find more about Solid performance in this article: SolidJS, the solution to build performant Web application!).
Example
If we go back to my feature:
Again the first logs correspond to the mounting of the component. If I replay the same interactions as in the React example:
- Modification of
showFullName
signal: the component is not fully recomputed but thecreateEffect
is executed sincedisplayName
has changed. - Modification of
lastName
signal: the component is still not recomputed,displayName
doesn’t change (showFullName
isfalse
), thereforecreateEffect
isn’t re-executed. - Modification of
showFullName
signal: the component is still not recomputed butcreateEffect
is re-executed sincedisplayName
has changed.
So if we compare logs :
React
SolidJS
It’s pretty obvious that contrary to React, Solid can manage state changes without recomputing the whole component.
Let’s dive deeper into SolidJS’s fine-grained reactivity mechanism.
Signals
You may have noticed something strange in my implementations. If you’re used to React and its states, you know that you access a state value the same way you would access any variable value:
const [firstName, setFirstName] = useState("John");
const [lastName, setLastName] = useState("Smith");
const [showFullName, setShowFullName] = useState(true);
const displayName = !showFullName ? firstName : `${firstName} ${lastName}`;
However with SolidJS, the signals are not variables, they are functions (you need to add parenthesis to access their values):
const [firstName, setFirstName] = createSignal("John");
const [lastName, setLastName] = createSignal("Smith");
const [showFullName, setShowFullName] = createSignal(true);
const displayName =() => {
if (!showFullName()) return firstName();
return `${firstName()} ${lastName()}`;
};
This rather small difference is the key to the entire SolidJS’s fine-grained reactivity.
Signals are based on the Observer design pattern (you can learn more about this pattern in Refactoring Guru’s article).
Each signal comes with a set of subscribers. The function createSignal
returns a method to read the signal and a method to edit it.
When you access a signal, for example in a custom function, or a createEffect
, it adds this function or this effect to the list of subscribers of the signal.
Then, if you edit the signal’s value, each of its subscribers will be recomputed and updated accordingly.
Signals and reactivity
Key takeaways
SolidJS is a new framework based on React syntax but with a huge difference in its way to synchronize your UI with your data. Where React uses a virtual DOM and recomputes each time the whole component and its children, SolidJS uses signals to make more local updates.
These two methods are very different paradigms and even if you might think that signals are much better (because faster) than virtual DOM it’s also less predictable. If you want to go further into the trade-offs between Solid and React I recommend the React vs Signals: 10 years later article from the creator of SolidJS and his discussion in comments with Dan Abramov (co-creator of Redux and Create React App).
For my part, I would not recommend switching from React to SolidJs in a production code. I think that for most web projects the performance issues are not due to virtual DOM and SolidJS needs a strong understanding of the reactivity to predict the components behaviour. Moreover, it’s a young framework with low documentation so far. However, I’ll definitely follow its growth since reactivity is at the core of rising frontend frameworks.