SolidJS「分割代入しないでクレメンス」ワイ「Why」

# SolidJS君さぁ...
Reactとは違い、`state`や`props`を分割代入すると、reactivityが失われます。どういうことかと言うと、
```jsx
const Component = (props) => {
  const { isLoading } = props
  return (
    <Show when={!isLoading} fallback={<div>Loading...</div>}>
      <div>Some Content</div>
    </Show>
  )
}
```
こうやって`isLoading`を分割代入すると、値が変わっても表示が切り替わらなくなります。
[`createSignal`](https://www.solidjs.com/docs/latest/api#createsignal)や[`createStore`](https://www.solidjs.com/docs/latest/api#createstore)によって作成された状態に関しても同じです。

## なぜなのか
公式ドキュメントの[このページ](https://www.solidjs.com/guides/reactivity#considerations)の2番目のポイントに詳しく書いてありますね。
個人的な解釈をざっくりと書きます。

### まず分割代入とは
`const { isLoading } = props`というのは言い換えると、`isLoading`という名前の変数に`props.isLoading`の値をアサインしているということです。
`const isLoading = props.isLoading`と書くとわかりやすいですね。
自分は、「`isLoading`という変数は、`props.isLoading`の一時的なエイリアスでしかない」と解釈しています。

### 更新されるのはpropsの値のみ
新しい値が渡された時、`props.isLoading`の値が更新されるのであって、`isLoading`というただのエイリアスが直接更新されるわけではありません。

### 関数が一度しか実行されないのが理由
```jsx
//この関数は一度しか実行されないから、
const Component = (props) => {
  const { isLoading } = props //isLoadingという変数に値がアサインされるのは最初にここでだけ。
  return (
    //isLoadingというエイリアスの値はずっと変わらないからビューも変化しない。
    <Show when={!isLoading} fallback={<div>Loading...</div>}>
      <div>Some Content</div>
    </Show>
  )
}
```
Reactなら再レンダリングの度に関数が上から下まで実行されるので、分割代入もその度に行われますが、SolidJSはそもそも再レンダリングが起きません。
`props.isLoading`の値が変わっても`isLoading`という「一時的なエイリアス」にアサインし直すことはありません。


## Reactivityを保持するためには
[さっきのページ](https://www.solidjs.com/guides/reactivity#considerations)によると
- jsxの中
- リアクティブな関数(`createEffect`など)の中

から直接`props`や`state`にアクセスすれば、値は再評価されるとあります。


```jsx
const Component = (props) => {
  return (
    // jsxの中で直接props.isLoadingにアクセスしているからちゃんと更新される。
    <Show when={!props.isLoading} fallback={<div>Loading...</div>}>
      <div>Some Content</div>
    </Show>
  )
}
```

## いちいち`props.`なんて書きたくないよ！
たしかに。
幸い解決法はあります。

### 関数で包んであげる
大事なのは、jsx(もしくはリアクティブな関数)の中で値にアクセスすることなので、関数で包んで、その関数をjsx内で呼び出せば解決します。

まあこれは分割代入とは違いますが、一つの値を`props`から取り出したい時に使えます。

```jsx
const Component = (props) => {
  const isLoading = () => props.isLoading
  return (
    <Show when={!isLoading()} fallback={<div>Loading...</div>}>
      <div>Some Content</div>
    </Show>
  )
}
```

実際に`props.isLoading`にアクセスしているのは、jsx内で`isLoading`という「関数」を呼んでいる時なので値はちゃんと更新されます。

少しまとめます。

```jsx
//jsx内でprops.someValueにアクセスしているので⭕
return (
  <div>{props.someValue}</div>
)
```

```jsx
//jsx外でprops.someValueにアクセスしているので❌
const { someValue } = props
return (
  <div>{someValue}</div>
)
```

```jsx
//関数を通して、jsx内でprops.someValueにアクセスしているので⭕
const someValue = () => props.someValue
return (
  <div>{someValue()}</div>
)
```

### ライブラリを使う
最初から言えよって感じですが。
[destructure](https://github.com/solidjs-community/solid-primitives/tree/main/packages/destructure)という、solidjs-communiyの方々が開発したライブラリです。(2022/6/10時点ではexperimental)
使い方はとってもシンプル。
```jsx
const Component = (props) => {
  const { isLoading } = destructure(props)
  return (
    <Show when={!isLoading()} fallback={<div>Loading...</div>}>
      <div>Some Content</div>
    </Show>
  )
}
```
注意すべき点は、さきほどの例と同じく`isLoading`は関数であるということです。

このライブラリに関しては別記事で詳しく解説するつもりですが、待てない方は[公式ドキュメント](https://github.com/solidjs-community/solid-primitives/tree/main/packages/destructure#solid-primitivesdestructure)を読んでください。

## `props`を2つに分けたい

「`props`の一部だけを子コンポーネントに渡したい」という場合、分割代入を使いがちですよね。
```jsx
const Component = (props) => {
  const { title, ...childProps } = props
  return (
    <>
      <h1>{title}</h1>
      <Child {...childProps} />
    </>  )
}
```

こういった場合に便利なのが`splitProps`です。
```jsx
const Component = (props) => {
  const [local, childProps] = splitProps(props, ['title'])
  return (
    <>
      <h1>{local.title}</h1>
      <Child {...childProps} />
    </>
  )
}
```

## デフォルトの値を設定したい
これも普段は分割代入を使いますよね。
```jsx
const Component = ({ title = 'default title', ...props }) => {
  return (
    <h1>{title}</h1>
  )
}
```
こういった場合には`mergeProps`を使います。
```jsx
const Component = (props) => {
  props = mergeProps({title: 'default title'}, props)
  return (
    <h1>{props.title}</h1>
  )
}
```


# 結論
分割代入ができないという制約を面倒くさいと思うかは人次第ですが、個人的にはそこまで大きな問題だとは捉えていません。
実を言うと`props.`と書くのは、値が`props`からの物だとひと目で分かるの好きですし。
クソデカオブジェクトをまるっと分割代入して、変数名が縦長に並ぶより`props.`と書くほうが好みです。
最悪[destructure](https://github.com/solidjs-community/solid-primitives/tree/main/packages/destructure)を使えばいいですしね。ちなみにdestructureがbuilt-inではない理由としては、「みんなそんなに分割代入しないでしょ」とのことです。ほんまか？

なにはともあれ、SolidJSでは分割代入しないでね！というお話でした。