SolidJS - An alternative to React?
Tags: solidjs, javascript, webdev, react
Published: 2022-03-31 14:06:04

## What is Solidjs? 

Today when I am surfing on internet searching for some highly performant frameworks for frontend, I found [solidjs](https://www.solidjs.com/), which is a simple and performant reactive building user interfaces.

the project github link is [here](https://github.com/Zhangjt9317/solidjs_example)

## Begin

while we can start using a [vite-plugin-solid](https://github.com/solidjs/vite-plugin-solid):

```bash
npx degit solidjs/templates/js my-solid-project
cd my-solid-project
npm install # or pnpm install or yarn install
npm run start # starts dev-server with hot-module-reloading
npm run build # builds to /dist
```

then you can manually install vite related plugins :

```bash
# with npm
npm install -D vite vite-plugin-solid babel-preset-solid
npm install solid-js

# with pnpm
pnpm add -D vite vite-plugin-solid babel-preset-solid
pnpm add solid-js

# with yarn
yarn add -D vite vite-plugin-solid babel-preset-solid
yarn add solid-js
```

after installing all those you will find in `vite.config.js` as 
```javascript 
// vite.config.ts
import { defineConfig } from 'vite';
import solidPlugin from 'vite-plugin-solid';

export default defineConfig({
  plugins: [solidPlugin()],
});
```

the code I use for some demo is here: 

```jsx

import logo from "./logo.svg";
import styles from "./App.module.css";

import {
  createEffect,
  createSignal,
  createMemo,
  createResource,
  Show,
  Switch,
  Match,
} from "solid-js";
import { render, Portal, Dynamic } from "solid-js/web";
const fetchUser = async (id) =>
  (await fetch(`https://swapi.dev/api/people/${id}/`)).json();

const User = () => {
  const [userId, setUserId] = createSignal();
  const [user] = createResource(userId, fetchUser);

  return (
    <>
      <input
        type="number"
        min="1"
        placeholder="Enter Numeric Id"
        onInput={(e) => setUserId(e.currentTarget.value)}
      />
      <span>{user.loading && "Loading..."}</span>
      <div>
        <pre>{JSON.stringify(user(), null, 2)}</pre>
      </div>
    </>
  );
};

function fibonacci(n) {
  if (n <= 1) return 1;
  return fibonacci(n - 1) + fibonacci(n - 2);
}

function Modal() {
  return (
    <div class="container">
      <Portal>
        <div class="popup">
          <h1>Popup</h1>
          <p>Some text you might need for something or other.</p>
        </div>
      </Portal>{" "}
    </div>
  );
}

function Compare() {
  const [x] = createSignal(7);

  return (
    <Switch fallback={<p>{x()} is between 5 and 10</p>}>
      <Match when={x() > 10}>
        <p>{x()} is greater than 10</p>
      </Match>
      <Match when={5 > x()}>
        <p>{x()} is less than 5</p>
      </Match>
    </Switch>
  );
}

function Cats() {
  // <For each={}> to handle for loop, it updates or moves DOM rather than recreating them
  const [cats, setCats] = createSignal([
    { id: "J---aiyznGQ", name: "Keyboard Cat" },
    { id: "z_AbfPXTKms", name: "Maru" },
    { id: "OUtn3pvWmpg", name: "Henri The Existential Cat" },
  ]);
  return (
    <For each={cats()}>
      {(cat, i) => (
        <li>
          <a
            target="_blank"
            href={`https://www.youtube.com/watch?v=${cat.id}`}
            style={{ color: "white" }}
          >
            {i() + 1}: {cat.name}
          </a>
        </li>
      )}
    </For>
  );
}
// wrong naming would not work here
function Btn() {
  const [loggedIn, setLoggedIn] = createSignal(true);
  const toggle = () => setLoggedIn(!loggedIn());

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

function Counter() {
  const [count, setCount] = createSignal(0);
  const fib = createMemo(() => fibonacci(count()));
  const doubleCount = () => count() * 2;
  createEffect(() => {
    console.log("the count is: ", count());
  });
  return (
    <>
      <button onClick={() => setCount(count() + 1)}>Click Me</button>
      {count()} <br />
      {doubleCount()} <br />
      fib: {fib()}
    </>
  );
}

function App() {
  return (
    <div class={styles.App}>
      <p>Counter demos how signal and createEffect / createMemo work</p>
      <Counter /> <p>Btn demos how simple conditional works in Solid</p>
      <Btn />
      <p>Cats demos how list rendering works using For tag </p>
      <Cats />
      <p>Compare demos how switch and match work</p>
      <Compare />
      <p>Modal demos how Portal works </p>
      <Modal />
      <p>Async demos </p>
      <User />
    </div>
  );
}

export default App;

```

> let's take a look one by one, this article will update 

## Entry and Components
* one thing I like solidjs is that `class` is `class` instead of `className`, and the most of others are the same to react jsx writing
* solidjs uses `render()` function to render, takes 2 args, first one is a function, second one is a container


## Signal and Effect / Memo

`signals` are the most basic reactive primitive in `solidjs`, they track a single value, which can be any JS object that changes over time.

the `createSignal` function returns a pair of functions as a two-element array: a getter and a setter, basically remind me of `useState`

here we define a `Counter`, we define `count` as a signal, and two derived signals from it, that is `fib` and `doubleCount`

let's show how reactivity works in solidjs here, clicking the button to add number, and both `doubleCount` and `fib` will update

```jsx
function Counter() {
  const [count, setCount] = createSignal(0);
  const fib = createMemo(() => fibonacci(count()));
  const doubleCount = () => count() * 2;
  createEffect(() => {
    console.log("the count is: ", count());
  });
  return (
    <>
      <button onClick={() => setCount(count() + 1)}>Click Me</button>
      {count()} <br />
      {doubleCount()} <br />
      fib: {fib()}
    </>
  );
}
```
`createEffect` in solidjs is used to create a effect as a function

the effect automatically subscribes to any signal that is read during the function's execution and returns when any of them changes --> kinda like Vue's `watch` 


## Flow, Show

JSX allows you to use JS to control the logic flow in the templates, but when virtual DOM is not in use here, use of `Array.prototype.map` would wastefully recreate all the DOM nodes on every update 

for a simple conditional display, solidjs provides a `Show` tag: 

```jsx 
<Show
  when={loggedIn()}
  fallback={<button onClick={toggle}>Log in</button>}
>
  <button onClick={toggle}>Log out</button>
</Show>
```
`fallback` prop acts as `else` and show when the condition passed to `when` is not true

example here: 

```jsx
// wrong naming would not work here
function Btn() {
  const [loggedIn, setLoggedIn] = createSignal(true);
  const toggle = () => setLoggedIn(!loggedIn());

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

## For, Index, Switch, Match

coming to more complex conditionals, we need its provided 


to render a list, due to the reason I mention above (no virtual DOM here), solidjs also provides `For` tag here

in this example we will render a list of index with its content

```jsx
function Cats() {
  // <For each={}> to handle for loop, it updates or moves DOM rather than recreating them
  const [cats, setCats] = createSignal([
    { id: "J---aiyznGQ", name: "Keyboard Cat" },
    { id: "z_AbfPXTKms", name: "Maru" },
    { id: "OUtn3pvWmpg", name: "Henri The Existential Cat" },
  ]);
  return (
    <For each={cats()}>
      {(cat, i) => (
        <li>
          <a
            target="_blank"
            href={`https://www.youtube.com/watch?v=${cat.id}`}
            style={{ color: "white" }}
          >
            {i() + 1}: {cat.name}
          </a>
        </li>
      )}
    </For>
  );
}
```

`Switch` and `Match` are used when we have to deal with conditionals with more than 2 mutual exclusive outcomes:

```jsx
function Compare() {
  const [x] = createSignal(7);

  return (
    <Switch fallback={<p>{x()} is between 5 and 10</p>}>
      <Match when={x() > 10}>
        <p>{x()} is greater than 10</p>
      </Match>
      <Match when={5 > x()}>
        <p>{x()} is less than 5</p>
      </Match>
    </Switch>
  );
}
```

well if you want to write tight and clean code, we can use `Dynamic` to write

```jsx
<Switch fallback={<BlueThing />}>
  <Match when={selected() === 'red'}><RedThing /></Match>
  <Match when={selected() === 'green'}><GreenThing /></Match>
</Switch>

# to this 

<Dynamic component={options[selected()]} />
```

## ErrorBoundary, Suspense 

well you can capture js error in the UI and fallback to what you want to display, first, define a `<Broken />`: 

```jsx
const Broken = (props) => {
  throw new Error("Oh No");
  return <>Never Getting Here</>
}
```

and then use it: 

```jsx
  <div>Before</div>
      <ErrorBoundary fallback={err => err}>
        <Broken />
      </ErrorBoundary>
      <div>After</div>
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zaguib1h5w9u9covulpc.png)
 


## Lifecycle (onMount, onCleanup, onError)

solidjs has few lifecycle APIs here

```jsx
onMount(async () => {
  const res = await fetch(`https://jsonplaceholder.typicode.com/photos?_limit=20`);
  setPhotos(await res.json());
});

const timer = setInterval(() => setCount(count() + 1), 1000);
onCleanup(() => clearInterval(timer));
```

## Conclusion

well, `solidjs` looks interesting, but it seems like Vue3 can do what it sells too, with much richer ecology and other stuff. I personally still stick to Vue.