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


useStateってありますよね

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

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

でもそれができたら面白くない?

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

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

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

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

どうやって使うの?

筆者が開発中のRealtimely(ベータ版※)を利用することですぐに試すことができます。
使い方は至ってシンプルで、ほとんどReactのuseStateと同じような使い心地にしています。

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

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

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

え?そんなの信じられないって?
デモページがあるから試してみてね。

どうやって動いてるの?

詳細はRealtimely docsに譲るとして、簡単に説明するとこうです。

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

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

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

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

まとめ

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

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