【React】面白いhooksを作ったよ！【全員が同じstateを共有する】

# useStateってありますよね

```typescript
const [state, setState] = useState({count:0, boolean:true})
```

Reactで最もよく使う状態管理を行うhook、 `useState`。めちゃくちゃ使い心地いいですよね。
でもこの`useState`は、当たり前ですが、**共有されません**。 沖縄県在住のAさんが`setState`で状態を変更しても、東京都在住のBさんの`state`には影響を与えません。 **当たり前ですね**。

# でもそれができたら面白くない？
![realtimeSharedState.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234396/660b2b93-3730-766b-841f-4f54d41a62e2.png)

誰かが`setState`を実行したら、それが**同じページを見ているすべての人に反映されたら**何か面白いことができそうじゃないですか？

# 例えば、オンライン勉強会とかができる

![page_sync.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234396/334bb992-0a49-837e-5694-b2d683306e40.gif)

上記のデモ画面を見てください。ブラウザを二つ立ち上げて、片方だけを操作しています。なのに、**もう片方のスライドも同期してスライドが切り替わっている**のが分かるでしょうか。

これはスライド番号を`state`として持っておいて、`setState`でスライド番号を切り替えると、同じURLを見ている人全員の`state`を切り替えることができています。
これによって、**みんなが同じスライドを見ている状態**を作ることができているので、**Webで完結するオンライン勉強会**ができそうな雰囲気が出てきましたよね（ワクワク)。

# どうやって使うの？

筆者が開発中の[Realtimely](https://kitaharamugiro.github.io/Realtimely/jp/docs/api-guide/useRealtimeSharedState)(ベータ版※)を利用することですぐに試すことができます。
使い方は至ってシンプルで、ほとんどReactの`useState`と同じような使い心地にしています。

以下サンプルコードを見てください。
`useRealtimeSharedState`というhook関数がrealtimelyにあるので、`useState`のように宣言してあげています。一つだけ違うのは、`useRealtimeSharedState`の第二引数に識別子の文字列を入れてあげるだけ。これは同じURL内で複数の共通stateを持ちたい時に別の識別子を指定して区別できるようにする必要があるからです。

ボタンを押すと`setSharedState`で状態が切り替わるような実装をしてあげれば、同じURLを見ている人すべての`sharedState`が切り替わるようになります。


```tsx
import React from "react";
import { useRealtimeSharedState } from 'realtimely';

const Home = () => {
    const [sharedState, setSharedState] = useRealtimeSharedState({ count: 1, boolean: true }, "realtime-state")

    const onClickButtonA = () => {
        setSharedState({ ...sharedState, count: sharedState.count + 1 })
    }

    const onClickButtonB = () => {
        setSharedState({ ...sharedState, boolean: !sharedState.boolean })
    }

    return (
        <div>
            <main>
                <button onClick={onClickButtonA}>{"setSharedState({...sharedState, count: sharedState.count+1})"}</button>
                <button onClick={onClickButtonB}>{"setSharedState({ ...sharedState, boolean: !sharedState.boolean })"}</button>


                <h1>
                    {JSON.stringify(sharedState)}
                </h1>
                <h3>
                    And This state is shared by everyone looking at this URL
                </h3>

            </main>
        </div>
    )
}

export default Home

```

**え？そんなの信じられないって？**
[デモページ](https://d2vfno2gco8009.cloudfront.net/realtime-shared-state)があるから試してみてね。


# どうやって動いてるの？

詳細は[Realtimely docs](https://kitaharamugiro.github.io/Realtimely/jp/docs/how-it-works/architecture)に譲るとして、簡単に説明するとこうです。

**「URL + 識別子」**を一意のキーにしたデータベースを作り、`setSharedState`が実行されたらそこに最新の`sharedState`を送信する。
同じURLを見ている人は常に**「URL + 識別子」**の値を監視しており、更新されたら最新の値をwebsocketで配信するようにする。
最新の値が配信されたら、ローカルの`sharedState`を最新に置き換える。

これによって、上記のような仕組みが出来上がります！！

# バックエンドを自分で作るにはどうする？

RealimelyはDynamoDBとAWS AppSyncを使って上記の仕組みを実装しています。 
CloudFormationなどで配布したいのですが、そこまで手が回っていないのが現状です。
他にもFirebaseのFirestoreでも同様の実装ができるのではないかと思いますので、やりたい人は思い思いにやってみてください。

# まとめ
同じURLを見ている人全員の`state`を同期できたら面白いことができそうじゃないかと思って仕組みを作ってみました。
この仕組みでこんなことができそう！みたいなアイディアがあったらコメント欄で教えてください！！！


※ 記事中で紹介したRealtimelyはベータ版であり、セキュアではありません。プロダクションで使用したい場合は自前で実装するか、Realtimelyの開発が進むように[支援](https://kitaharamugiro.github.io/Realtimely/jp/docs/donation)してください😄