ReactもSvelteも大好きな私が思うSolidJSの良いところ

# SolidJSとは
https://www.solidjs.com/
仮想DOMを使わない、Reactライクに書ける新しいフロントエンドのフレームワークです。
同じく仮想DOMを使わないフレームワークとしてSvelteがありますが、Svelteと違ってReactと近い書き方で記述できるのが特徴です。
今回はReactもSvelteもがっつり使っている私が思うSolidJSの良いところ・もうちょっと改善してほしいところを挙げていきます。

## SolidJSの良いところ
### 高パフォーマンス
SolidJSの最大のポイントは仮想DOMを使わずに直接DOMを更新することで高パフォーマンスを発揮することです。
Svelteと同様にコンパイル時点で、このステートが更新されたらこのDOMを更新するというのを紐づけることで仮想DOMいらずにReactのようなアプリケーションを作ることが出来ます。
公式にパフォーマンス比較の画像が載っていますが、Vanillaとほぼ変わらないパフォーマンスが出るそうです。（Svelteよりも高速）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272803/55754641-8b52-6e0c-e80e-156ddfdab534.png)

### createSignalが便利
SolidJSではステートの扱いがより柔軟になります。
その一例として、`createSignal`の柔軟さが挙げられます。
この`createSignal`はReactでいうところの`useState`に近いですが、大きな違いとしてコンポーネントの外でも使えるというところがあります。

```tsx
import { createSignal } from 'solid-js';

const [outerState, setOuterState] = createSignal('');

const Comp = () => {
    const [innerState, setInnerState] = createSignal('');

    return (
        <div>
            {outerState()}, {innerState()}
        </div>
    );
};
```

こちらの例では、`outerState`はコンポーネントの外、`innerState`はコンポーネントの中で定義していますがどちらも同じように扱うことができます。
二つの違いはこの`Comp`というコンポーネントが複数回呼ばれた場合に、`outerState`の方は一つのステートを複数のコンポーネントが参照する形になりますが、`innerState`の場合はそれぞれのコンポーネントに特有のステートが生成されます。
Reactでいう`Redux`や`Context`のようにグローバルに近いレイヤーでのステート宣言が可能になります。
（SolidJSの場合はこれとは別に`Store`や`Context`の機能があり、また別の使い分けをすることが出来ます。）

### createEffectも便利

もう一つ便利な点として、ステートが変更された場合にコールバックを実行する`createEffect`という関数が、Reactの`useEffect`と違って二つ目の引数（依存関係の配列）を必要としません。
こちらも柔軟に設定が可能で、例えば`b`というステートの変更は検知しなくていいが処理の中では使いたいという場合には`untrack`という関数を使うことで可能になります。
（Svelteではこの辺が非常にめんどくさいので、SolidJSはよく考えられて作られていることが伺えます。）

```tsx
const [a, setA] = createSignal('');
const [b, setB] = createSignal('');

createEffect(() => {
    console.log(a());
});

createEffect(() => {
    const v = a();
    untrack(() => console.log(v, b()));
});

```

### カスタムディレクディブ
Vue/Svelteにあるようなdirective機能もSolidJSでは使えます。
以下の例ではよく実装するようなデータバインディングをしてくれる`model`というdirectiveを作成しています。


```tsx
const [name, setName] = createSignal('');

function model(el: HTMLInputElement, value: () => Signal<string>) {
    const [field, setField] = value();
    createRenderEffect(() => (el.value = field()));
    el.addEventListener('input', (e) => setField(e.target.value));
}

<input type="text" use:model={[name, setName]} />;
```

### JSXの利便性
SvelteやVueと違い独自の拡張子/フォーマットで書くのではなくJSXでコンポーネントを書けるも便利な点です。
1ファイル1コンポーネントの制約がないので、1ファイルでいくつもコンポーネントを書けます。
またReactで便利だったHOC（高階コンポーネント）のようなことも実装することができます。

```tsx
const C: Component<{
    testSignal: Signal<string>;
}> = ({}) => {
    return <></>;
};

const wrap = (
    C: Component<{
        testSignal: Signal<string>;
    }>
) => {
    const WrappedComponent: Component<{}> = () => {
        const testSignal = createSignal('');

        return <C testSignal={testSignal} />;
    };

    return WrappedComponent;
};

export const Wrapped = wrap(C);
```

### useRefが必要ない
SolidJSの場合はReactの`useRef`が必要なく、以下のように直感的に書くことができます。

```tsx
const CompRef = () => {
    let el: HTMLDivElement | undefined = undefined;
    return <div ref={el}></div>;
};
```

### ErrorBoundary/Portal
Reactにもあった便利なコンポーネントがSolidJSでも標準でそろっています。
例えばコンポーネント内のエラーをキャッチしてくれる`ErrorBoundary`やDOMツリーの外にコンポーネントをレンダリングするときに使える`Portal`などがあります。
これらの機能はSvelteでは存在しなくて困っていいたので非常に助かりました。

### その他細かい良い点
* チュートリアルがしっかりしている
    * 日本語がやや怪しいけど充分読める
    * 入門までは以下のURLから簡単にできる
    * https://www.solidjs.com/guides/getting-started
* ライフサイクル（onMount, onCleanup, onError）
    * この辺はほとんどReactと同じ

## SolidJSの改善点
### 周辺ライブラリが少ない
まだ発展途上のフレームワークではあるので周辺ライブラリは圧倒的に少ないです。
特にアプリケーションを作るうえでは必須とも言っていいUIコンポーネントのライブラリはほとんど存在せず、solid-bootstrapというものだけ見つかりました。
https://solid-libs.github.io/solid-bootstrap/

この辺はライブラリが発展していくとともに充実していくことを期待しています。

### props周りがややこしい
props周りでReactでやっていたような扱いをするとSolidJSのReactivityが失われるので注意です。
例えば次のような書き方では意図通りに動きません。

```tsx
const C: Component<{ loading: boolean }> = (props) => {
    const { loading } = props;
    return <>{loading}</>;
};
```

ここでは親コンポーネントが`loading`というプロパティを変更すれば、子コンポーネントの中身が変更されることを期待しますが、実際にはそうはなりません。
変更されるのはあくまでも`props`だけになっていしまうので`const { loading } = props;`とした時点で、`loading`のReactivityは失われます。

Reactivityを保持するには、`props.xxx`と書くか、関数として定義するかの2択になります。

```tsx
const C: Component<{ loading: boolean }> = (props) => {
    return <>{props.loading}</>;
};

const C: Component<{ loading: boolean }> = (props) => {
    const loading = () => props.loading;
    return <>{loading()}</>;
};
```

この辺がややこしいので少し改善されるととっつきやすくなるかと思います。

### styleのcamelCaseが出来ない
細かい点ですが、styleを指定する際に`font-size`のように途中にハイフンが入るような場合、Reactだと`fontSize`という風にcamelCaseで指定できたのが、SolidJSだと`'font-size'`という風にする必要があるのが地味に面倒だったりします。
オブジェクトで指定できるだけSvelteよりはマシなんですが、、、

```tsx
const C: Component<{}> = (props) => {
    return (
        <div
            style={{
                'font-size': 12
            }}
        />
    );
};
```

# 最後に
少し前にSvelteに出会った際にすごい！と思いましたが、だんだん使っていくとReactだとこれができたのになーと思う部分もありました。
SolidJSはReactとSvelteの良いところどりをしているような感じがしたのでこれからもっと使っていこうと思います。