Introduction to SolidJS for React developers
Tags: solidjs, react, javascript, webdev
Published: 2022-04-20 07:44:04

SolidJS has been gaining traction as a UI library for building web applications that are extremely fast and small. At the first glance, Solid doesn’t appear much different from React. Solid also uses JSX, it has an API that resembles React hooks, and it follows the same philosophy as React with unidirectional data flow, immutable interfaces and so on.

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

function App() {
  const [count, setCount] = createSignal(0);
  const interval = setInterval(() => setCount((count) => count + 1), 1000);
  onCleanup(() => clearInterval(interval));

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

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

But don’t let the look deceive you because Solid is fundamentally different. First of all, it doesn’t use Virtual DOM diffing to update the UI. Instead, Solid relies on reactive primitives that hold application state and automatically track dependencies, so when a piece of data changes, it knows immediately and exactly what needs to update. This fine-grained reactivity system allows Solid to consistently top speed and memory [benchmarks](https://github.com/krausest/js-framework-benchmark?ref=hackernoon.com) for UI libraries.

Secondly, Solid takes a pre-compilation approach in which it uses a compiler to set up the reactive graph and handle treeshaking to reduce bundle size. Thanks to this compiler, Solid applications are among the smallest in [comparison](https://levelup.gitconnected.com/a-solid-realworld-demo-comparison-8c3363448fd8) to other UI libraries.

This article aims to help React developers leverage their existing knowledge to learn the fundamentals of SolidJS. The article covers the following topics:

1. Defining components
2. Component state
3. Component lifecycle
4. Component communication
5. Event handling
6. Working with refs
7. Error handling
8. Code reuse

## Defining components

In a Solid application, components are functions that return JSX elements. Class components are not supported. Note that JSX code is compiled into functions that directly update the DOM (since Solid doesn’t use a Virtual DOM). To avoid recreating DOM nodes on every update, Solid provides several components for conditional and looping that we should use instead of `if/else`, `switch` statements and `Array.prototype.map`. The most important components are `Show`, `Switch` and `For`:

```typescript
<Show
  when={loggedIn()}
  fallback={<button onClick={toggle}>Log in</button>}
>
  <button onClick={toggle}>Log out</button>
</Show>
```

```typescript
<Switch fallback={<p>Normal temperature</p>}>
  <Match when={temp() >= 40}>
    <p>Too hot</p>
  </Match>
  <Match when={temp() <= 10}>
    <p>Too cold</p>
  </Match>
</Switch>
```

```typescript
<For each={articles()}>{(a, index) =>
  <li>{index() + 1}: {a.title}</li>
}</For>
```

## Component state

The cornerstones of reactivity in Solid are _signals_ and _effects_ which look somewhat similar to React’s `useState` and `useEffect` hooks:

```typescript
import { createSignal, createEffect } from "solid-js";

function App() {
  const [count, setCount] = createSignal(0);
  createEffect(() => {
    console.log("Count: ", count());
  });

  return <button onClick={() => setCount((c) => c + 1)}>{count}</button>;
}
```

However, signals are vastly different from the `useState` hook in the following aspects:

* While you can only call `useState()` from within a function component or a custom hook, you can call `createSignal()` from anywhere. If called within a component, the signal represents that component’s local state. Otherwise, the signal represents an external state that any component can import and use to render their UI.

* More importantly, signals automatically track functions that depend on its data and will invoke these functions whenever the data changes. Note that the first element in the tuple returned by `createSignal()` is not the data itself but a getter function. When the getter function is called, the calling function (obtained from a global stack) will be added to the signal’s subscribers list.

Similar to React's `useEffect` hook, `createEffect()` defines a side effect that should run whenever a signal it depends on changes. However, thanks to Solid’s automatic dependency tracking, you don’t have to explicitly provide a dependency list.

## Component lifecycle

With React, your component function reruns whenever the component’s state changes. In contrast, Solid component functions never rerun. A component runs only once to create the necessary signals and effects (JSX code is compiled into an effect as well). After that the component vanishes. That means we don’t have access to component lifecycle events like we do with React or other libraries.

However, Solid does provide two special events called `onMount` and `onCleanup`. `onMount` can be considered a special effect that runs only once, after all initial rendering is done. The most common use case is fetching data when a screen is loaded.

```typescript
import { createSignal, onMount } from "solid-js";

function App() {
  const [data, setData] = createSignal();

  onMount(async () => {
    const res = await fetch(`/path/to/your/api`);
    setData(await res.json());
  });

  return (/* JSX to render UI based on data */);
}
```

`onCleanup` can be called in a component (see the first example above), in an effect (example below), or at any scope that is part of the synchronous execution of the reactive system. `onCleanup` will run when that scope is disposed or re-evaluated.

```typescript
import { createSignal, createEffect, onCleanup } from "solid-js";

function App() {
  const [counting, setCounting] = createSignal(false);
  const [count, setCount] = createSignal(0);

  createEffect(() => {
    if (counting()) {
      const c = setInterval(() => setCount((val) => val + 1), 300);
      onCleanup(() => clearInterval(c));
    }
  });

  return (
    <div>
      <button type="button" onClick={() => setCounting((val) => !val)}>
        {counting() ? "Stop" : "Start"}
      </button>
      <p>Counter: {count()}</p>
    </div>
  );
}
```

## Component communication

In this regard, Solid is pretty much the same as React. You use props to pass data from a parent component to a child (or pass actions back to the parent). Use Context API to pass data to descendent components.

{% embed https://codesandbox.io/embed/solidjs-context-1g3spw %}

However, there is a caveat. Generally, you should not destructure props. By doing so you will loose reactivity, meaning that the child component’s UI will not update when prop values change. As compensation, Solid provides two helpers for working with props: `mergeProps()` and `splitProps()`.

```typescript
// DON'T do this
function Greeting({ name, greeting = "Hi" }) {
  return <h3>{greeting}, {name}!</h3>
}

// use mergeProps() to set default values
function Greeting(props) {
  const merged = mergeProps({ greeting: "Hi" }, props);
  return <h3>{merged.greeting}, {merged.name}!</h3>
}
```

```typescript
// DON'T do this
export default function Greeting(props) {
  const { greeting, name, ...others } = props;
  return <h3 {...others}>{greeting}, {name}!</h3>
}

// use splitProps() instead of the rest syntax
function Greeting(props) {
  const [local, others] = splitProps(props, ["greeting", "name"]);
  return <h3 {...others}>{local.greeting}, {local.name}!</h3>
}
```

## Event handling

Like React, Solid only supports unidirectional data flows. There is no builtin mechanism for input binding. Unlike React, however, Solid applications use DOM events directly rather than synthetic events.

```typescript
function App() {
  const [name, setName] = createSignal("World");

  return (
    <div>
      <input
        type="text"
        value={name()}
        onInput={(evt) => setName(evt.currentTarget.value)}
      />
      <p>Hello, {name()}!</p>
    </div>
  );
}
```

## Working with refs

Using refs in a Solid application is not much different from that with React. Basically, you can either declare a local variable and assign it to a prop named `ref`, or use a callback:

```typescript
// local variable
function SimpleForm() {
  let ref;
  onMount(() => ref.focus());
  return (<input ref={ref} />);
}

// ref callback
function SimpleForm() {
  return (
    <input ref={el => {
      onMount(() => el.focus())
    }} />
  );
}
```

## Error handling

Another idea that Solid borrows from React is error boundary components. However, you don’t have to implement it manually as `ErrorBoundary` is a builtin component in Solid:

```typescript
import { ErrorBoundary } from "solid-js";

<ErrorBoundary fallback={err => {
  // report error
  console.log(err);

  // fallback UI
  return (/* JSX */)
}}>
  {/* your component tree */}
</ErrorBoundary>
```

## Code reuse

In React, you have multiple techniques for code reuse with the most popular being higher-order components, render props, and custom hooks. You can use similar techniques with Solid as well. The examples below are three implementations for a reusable self-running clock that we can easily use with different UIs.

**Higher-order component (HOC)**
{% embed https://codesandbox.io/embed/solidjs-hoc-qvxbt7 %}
**Render prop**
{% embed https://codesandbox.io/embed/solidjs-render-props-6dkrqs %}
**React hook-like code reuse**
{% embed https://codesandbox.io/embed/solidjs-hook-equiv-yktf76 %}
In addition to these techniques, Solid allows you to define reusable behavior as _custom directives_. A directive is a function that gets called when the element it decorates is added to the DOM. The function takes two arguments: the DOM element, and a getter function to obtain the directive's parameter. Below is an example directive which detects if user clicks outside of the element's boundary:

{% embed https://codesandbox.io/embed/solidjs-directive-eyb6ss %}

## Conclusion

SolidJS offers incredible performance and very small bundle size while being able to retain a simple programming model that we all love. As a relatively new library, Solid’s ecosystem and community are still small but that may change as more people get to know its potentials. As at this writing, Solid’s GitHub repo has got 16 thousand stars and the project is sponsored by reputable companies such as Cloudflare, Netlify, Vercel…

This article has covered only the fundamental topics on using SolidJS. I hope it can save you some time if you want to give Solid a try. The coming articles will address more in-depth topics such as state management, Suspense API, and server rendering. See you then!

Note: This article was first published to HackerNoon under a [different title](https://hackernoon.com/meet-solid-a-lightweight-javascript-ui-library-that-is-gaining-in-popularity). Republished here with the original title and an additional part on custom directives.