✌️

Reactでじゃんけんアプリをつくった

2021/05/14に公開

皆さん、じゃんけんをしたいのに、相手がいない! 困った! なんてことありませんか?
そんなお悩み解消のために、React歴2週間の経験を活かして、こんなのをつくりました。

https://thawing-fjord-72897.herokuapp.com/

一人でもじゃんけんができるWebサービスです。
これでいつでもじゃんけんができますね。

ソースは↓なので、ご自由にお使いください。

https://github.com/0si43/rock-paper-scissors

技術スタック

使用言語: JavaScript
ライブラリ: React
デプロイ環境: Heroku(無料)

メインロジックは147行程度です。
あとは少々CSSをいじって、レイアウトを整えました。

https://github.com/0si43/rock-paper-scissors/blob/main/src/index.js

ざっくり解説していきます

コンポーネント設計の勘所が全然分かってないのですが、チュートリアルの知識だけでなんとなく設計しました。
命名はあんま良くないな〜と思いましたが、この規模のWebアプリならまあいいかと。

App: 最上位コンポーネント。状態変数を管理する
Page: ページのレンダリングを行うコンポーネント
PlayerHand: ユーザーが出す手
OpponentHand: 相手の手

仕様

じゃんけんアプリの仕様としては、

  1. プレイヤーが自分の手を選ぶまで相手の手がグーチョキパーの順で変わる
  2. プレイヤーが自分の手を選んだら、そのときの相手の手と勝敗を判定して、勝ち/負け/あいこを表示する
  3. 画面をタップすると1.に戻る

となっています。
AppplayerHandは初期状態では空文字が入っているので、これを利用して、
空文字だったら1.、手が入っていたら2.としてレンダリングするようにしました。

相手の手をランダムで動かすアニメーション

こだわりポイントとして、相手の手をアニメーションで動かしたいと思っていました。
「React アニメーション」でググると、追加でライブラリ入れろとか、CSSアニメーションを使えとか、色々出てきて混乱しましたが、
よくよく考えてみると、タイマー処理で状態変数を一定間隔で変更すれば、
そのたびにReactはレンダリングが走るので、アニメーションっぽく画面が更新できることに気づきました。

  setTimer() {
    this.timerID = setInterval(
      () => this.changeOpponentHand(),
      500
    );
  }

  changeOpponentHand() {
    if (this.state.playerHand) { return; }

    if (this.state.opponentHand === "✊") {
      this.setState({
        opponentHand: "✌️"
      });
    } else if (this.state.opponentHand === "✌️") {
      this.setState({
        opponentHand: "✋"
      });
    } else if (this.state.opponentHand === "✋") {
      this.setState({
        opponentHand: "✊"
      });
    }
  }

もうちょっと頭使ったら、条件分岐をすっきり書けそうな気もしますが、愚直に書きました。

コンポーネント分割でハマった

基本的には割とサクサク進んだ開発でしたが、一個だけハマったのが、
propsで親コンポーネントから子コンポーネントにメソッド渡したときに、思ったように動かなくて半日ぐらいとられました。

元々親コンポーネントでsetPlayerHand(hand)という処理をやっていて、動くのを確認してから、
一回じゃんけんで勝負がついた後のリセットを入れたいので、Pageという子コンポーネントをつくって、

    const page = <Page 
                  playerHand={playerHand}  
                  opponentHand={opponentHand}
                  onClick={(hand) => this.setPlayerHand(hand)}
                />
   if(playerHand) {
      return (
        <div className="app" onClick={this.startNewGame}>
          {page}
        </div>
      );
    } else {
      return (
        <div className="app">
          {page}
        </div>
      );
    }

という感じで分岐しています。

ここも本当は三項演算子使って、

<div className="app" onClick= { playerHand ? this.startNewGame : null }>

みたいな書き方がしたいんですが、できなかったので、こんな書き方になっています。

最初、AppPageに対して、

onClick={() => this.setPlayerHand}

でメソッドを渡していて、これで渡すと子コンポーネントから引数指定しても、SyntheticEventが渡されるという謎の挙動になりました。
全然わからなかったので、もはやイベント変数経由で値渡せないかとか調べましたが、それはそれで挫折しました。

で、

onClick={(hand) => this.setPlayerHand(hand)}

この書き方だと、正しく引数が渡せました。

この辺のアロー関数の理解がそもそも甘いので、本格的にWeb開発やるならまずJSの基本(ES6)からやりたいですね。

Reactはなんかテンション上がる

このじゃんけんアプリは当初Ruby on Railsでつくろうと思っていたネタなんですが、結局Railsでは開発しませんでした。
RailsはRailsで全然整理されたフレームワークだと感じましたが、なんかテンションが上がる感じはなかったです。

Reactは触ってるとなんかテンションが上がる感じがありました。
なんでしょうね。
「このリッチな表現がこれだけのコードでできたよオイ」みたいな感覚があるんですよね。
もちろん宣言的UI特有の大変さはあるので、とっつきやすさは圧倒的にRailsに軍配が上がるんですが、
Reactはとっつきづらい分、その見返りがしっかりある感じがして、体感がよかったです。

流行ってるフレームワークだから、というのもあるんでしょうけど、
それ以上に流行る理由としての合理性・必然性みたいなところが感じられました。

今後

とりあえずReactそのものは完全に理解できたので、今度はもうちょっと本格的なものをつくりたい気持ちがあります。
自分のWebサイト(ポートフォリオ+個人ブログみたいなイメージ)をつくるのを妄想しています。
中身全然わかりませんが、Next.jsとVercelを使うと良さそうなので、つくってみたいです。

本格的に開発するなら、型チェックがないしんどさがだいぶあったので、TypeScriptの導入もしたいです。

Webフロントももっとやりたいですが、個人開発用のデプロイ環境ももっと良くしたい気持ちもあります。
HerokuはHerokuでお手軽で素晴らしいんですが、やはり無料アカウント故の限界もあるので、デプロイ環境をもうちょい調べたい気持ちがあります。

来週のテーマ

ゴニョゴニョWeb技術の野望を書きましたが、来週はFlutterをやりたい思ってます。
Flutterも名前しか知らないので、まずは何をやったらいいかを調べるところからのスタートですね。

Discussion