Reactサンプルアプリ開発 プラニングポーカー (8) WebSocketで通知する

親プレイヤーが開いた場に子プレイヤーが参加した際に、参加中にプレイヤーに対して通知を行って画面更新を行わたい。それにはWebSocketによる双方向通信が必要だ。

サーバー側(Flaskアプリケーション)は、Flask-SocketIOを用いる。まずはインストール。

(venv) $pip install flask-socketio

インポート(1行目)とインスタンス生成(6行目)。

from flask_socketio import SocketIO, emit
import uuid

app = Flask(__name__, static_folder="public", static_url_path="/")
app.secret_key = "planningpoker"
socketio = SocketIO(app)

場への参加は、通常のHTTPアクセスに対するハンドラが処理を受ける。12行目で、WebSocket通信によるブロードキャストを行う関数を呼び出している。

@app.route("/table/<table_id>/join", methods=["POST"])
def join_do(table_id):
    player_name = request.form["nickname"]

    table = tables[table_id]  # 場の情報
    players = table["PLAYERS"]
    player_id = len(players) + 1  # 現在の人数+1
    player = (player_id, player_name)
    players.append(player)
    session["ses_player_id"] = table_id + "." + str(player_id)

    broadcast_join(players)

    return redirect(f"/table/{table_id}")

broadcast_join関数の定義は以下のとおり。

def broadcast_join(players):
    print("broadcasting on player joinning..")
    player_names = [name for (id, name) in players]
    payload = {"players": player_names}
    socketio.emit("update", payload, broadcast=True)

JSONのペイロードを作成し、SocketIO.emitを呼び出してイベント送出を行う。第1引数はイベント名を表す。

次にクライアント側。こちらはSocket.ioを利用するのでまずはインストール。

$yarn add socket.io
$yarn add -D @types/socket.io-client

tsxファイルでインポート。

import io from 'socket.io-client'

Socketの作成。これはどこに定義すべきか悩ましいが、とりあえず関数コンポーネントの外側に定義しておいた。

const socket: SocketIOClient.Socket = io();

WebSocketのイベント待受けは副作用なので、useEffect内で行う。

export const TablePage: FunctionComponent<TableProps> = (props) => {
  const [bidding, setBidding] = useState(true);
  const [players, setPlayers] = useState<PlayerProps[]>([]);

  // ...

  useEffect(() => {
    socket.on("update", (data: any) => {
      const players = mapPlayers(data.players);
      console.log(`Got ${players.length} players on receiving an update event.`)
      setPlayers(players);
    });
  }, []);

socket.onでイベント名(update)を指定してコールバックを登録する。
setPlayersuseStateを用いて作成したステート変数更新関数であり、これを呼び出してWebSocketで受け取ったプレイヤー情報を更新する(12行目)。
イベントハンドラ登録は1回だけ行うようにしたいので、useEffectの第2引数にはから配列を渡している(13行目)。

WebSocketによる最初の双方向通信が実現できた。

ここまで開発したアプリケーションだが、ローカルでは問題なく動作するのにHerokuにデプロイするとうまく動かない。開いた場の情報をapp.pyに格納しているのが問題かもしれない。

Flaskでのアプリケーションスコープの状態管理がよくわからないので、Redisに格納するようにしようかと思う。次はその辺の実装だ。

コメント

タイトルとURLをコピーしました