React入門その④ ログイン画面(Mustache.jsとの比較)

2020-01-13

動画でReactの学習をしているのですが、いくら手を動かしながらとはいえ、動画教材を見終わったあとに、実はコード書けませんとなりそうで不安です。

そこで、作成中途のチャットアプリのログイン画面だけ、Mustache.jsからReactに書き換えて見ることにしました。

わかったこと

  • HTMLとJavaScripのコード行数の合計は、Reactのほうがはるかに長くなった(53行対109行)。
  • JSXはHTMLに似ているが、HTMLと同じではない

書き換えたページ

  • このような簡単なものです。
  • オープン中の部屋は今はリアルタイム更新はしません。ブラウザの再読み込みで更新。
ログイン画面

HTMLファイル

  • HTMLファイルはReactのほうが短いです。
  • 35行:Mustache.js
  • 16行:React

Mustache.js

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0">
    <link rel="shortcut icon" href="/img/favicon.ico" type="image/x-icon">
    <link rel="stylesheet" href="/css/styles.css">
</head>
<body>
    <div class="centered-form">
        <div class="centered-form__box">
            <h1>ピータンチャット</h1>
            <form action=/chat.html>
                <label>表示名</label>
                <input type="text" name="username" placeholder="表示される名前(必須)" required>
                <label>ルーム</label>
                <input type="text" name="room" placeholder="ルーム名(必須)" required>
                <button>参加</button>
            </form>
            <div id="rooms" class="login__rooms">
            </div>
        </div>
    </div>
   <script id="room-list-template" type="text/html">
        <h2 class="room-list-title">オープン中の部屋</h2>
        <ul class="rooms">
            {{#rooms}}
                <li>{{room}} ({{num_users}})</li>
            {{/rooms}}
        </ul>
    </script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/3.0.1/mustache.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/qs/6.6.0/qs.min.js"></script>
    <script src="/socket.io/socket.io.js"></script>
    <script src="/js/login.js"></script> 
</body>
</html> 

React

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0">
    <link rel="shortcut icon" href="/img/favicon.ico" type="image/x-icon">
    <link rel="stylesheet" href="/css/styles.css">
</head>
<body>
    <div id="app" class="rooms"></div>
    <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/qs/6.6.0/qs.min.js"></script> 
    <script src="/socket.io/socket.io.js"></script>
    <script src="/js/login.js"></script>
</body>
</html> 

JavaScriptファイル

  • JavaScriptファイルはMustache.jsのほうが短いです。
  • 18行:Mustache.js
  • 93行:React

Mustache.js

 // ログイン画面(index.html)のスクリプト
const socket = io()

const roomListTemplate = document.getElementById('room-list-template').innerHTML

socket.emit('getRoomList', (rooms) => {
    if (!rooms) {
        alert(error)
    }

    console.table(rooms)

    const html = Mustache.render(roomListTemplate, {
        rooms
    })
    
    document.getElementById('rooms').innerHTML = html
}) 

React

// ログイン画面(index.html)のスクリプト
const socket = io();

class ChatApp extends React.Component {
  constructor(props) {
    super(props);
    this.handleGetRoomList = this.handleGetRoomList.bind(this);
    this.state = {rooms: []}; 
  }

  handleGetRoomList() {
    let roomList = [];
    socket.emit('getRoomList', (roomList) => {
      if (!roomList) {
        alert(error);
      }

      console.table(roomList);
      
      this.setState(() => {
        return {
          rooms: roomList
        };
      });
    });
  }

  render() {
    const title = 'ピータンチャット';

    return (
      <div className="centered-form">
        <div className="centered-form__box">
          <Header title={title} />
          <Action />
          <Rooms
            rooms={this.state.rooms}
            handleGetRoomList={this.handleGetRoomList}
          />
        </div>
      </div>
    );
  }
}

ChatApp.defaultProps = {
  rooms: []
};

const Header = (props) => {
  return (
    <div>
      <h1>{props.title}</h1>
    </div>
  );
}

const Action = (props) => {
  return (
    <div>
      <form action="/chat.html">
        <label>表示名</label>
        <input type="text" name="username" placeholder="表示される名前(必須)" required></input>
        <label>ルーム</label>
        <input type="text" name="room" placeholder="ルーム名(必須)" required></input>
        <button>参加</button>
      </form>
    </div>
  );
}

const Rooms = (props) => {
  return (
    <div>
      <h2 className="room-list-title">オープン中の部屋</h2> {props.rooms.length === 0 && 'なし'}
      <ul className="rooms">
        {props.rooms.map((room) => (
            <Room key={room.room} roomText={room.room} numUsers={room.num_users} />
        ))}
      </ul>
    </div>
  );
}

const Room = (props) => {
  return (
    <div>
      <li>{props.roomText} - {props.numUsers}</li>
    </div>
  );
}

ReactDOM.render(<ChatApp />, document.getElementById("app"));