チャットシステムの完成

概要

Roomを作成することが出来るようになったので、あとはRoom内でMessageを作成/列挙できるようにすれば、チャットルームを完成させることが出来ます。

新しいクラス群、「MessagePane」「MessageList」「Message」「MessageForm」を作成して、Room内でメッセージが投稿できるようにしましょう。

実装

/assets/react/Main.js

'use strict';

import React from 'react';
import _ from 'lodash';

import RoomPane from './RoomPane';
import MessagePane from './MessagePane';


export default class Main extends React.Component {
  constructor() {
    super();

    this.state = {
      rooms: [{
        id: 1,
        name: 'room1',
        messages: [ {id: 1, content: 'asdf'} ]
      }, {
        id: 2,
        name: 'asdf',
        messages: [ {id: 2, content: 'qwer'} ]
      }],
      currentRoom: null
    };
    this.state.currentRoom = this.state.rooms[0];
  }


  _addRoom(roomName) {
    console.log(`addRoom: ${roomName}`);

    const newRooms = [].concat(this.state.rooms);
    newRooms.push({
      id: newRooms.length + 1,
      name: roomName,
      messages: []
    });

    this.setState({
      rooms: newRooms
    });
  }


  _changeRoom(roomId) {
    console.log(`changeRoom: ${roomId}`);

    const newState = _.cloneDeep(this.state);
    newState.currentRoom = _.find(newState.rooms, (room) => {
      return room.id === roomId;
    });

    this.setState(newState);
  }


  _addMessage(message) {
    console.log(`addMessage: ${message}`);

    const newState = _.cloneDeep(this.state);
    newState.currentRoom = _.find(newState.rooms, (room) => {
      return room.id === this.state.currentRoom.id;
    });
    newState.currentRoom.messages.push({
      id: newState.currentRoom.messages.length + 1,
      content: message
    });

    this.setState(newState);
  }


  render() {
    return (
      <div id="page">
        <RoomPane rooms={this.state.rooms} onRoomChange={this._changeRoom.bind(this)} onAddRoom={this._addRoom.bind(this)} />
        <MessagePane room={this.state.currentRoom} onAddMessage={this._addMessage.bind(this)} />
      </div>
    );
  }
}

/assets/react/RoomPane.js

'use strict';

import React from 'react';


export default class RoomPane extends React.Component {
  render () {
    return (
      <div id="sidebar">
        <h2>Rooms ({this.props.rooms.length})</h2>
        <RoomList {...this.props} />
        <RoomForm {...this.props} />
      </div>
    )
  }
}


class RoomList extends React.Component {
  render () {
    const roomNodes = this.props.rooms.map((room) => {
      return ( <Room key={room.id} {...room}  onRoomChange={this.props.onRoomChange} /> );
    });

    return (
      <ul id="roomList">
        {roomNodes}
      </ul>
    )
  }
}


class Room extends React.Component {
  _handleClick(e) {
    this.props.onRoomChange(this.props.id);
  }

  render () {
    return (
      <li>
        <p onClick={this._handleClick.bind(this)}>{this.props.name}</p>
      </li>
    )
  }
}


class RoomForm extends React.Component {
  constructor() {
    super();
  }

  _handleClick(e) {
    this.props.onAddRoom(React.findDOMNode(this.refs.inputText).value);
  }

  render () {
    return (
      <div>
        <h3>Create Room</h3>
        <input type="text" ref="inputText"></input>
        <button onClick={this._handleClick.bind(this)}>Create Room</button>
      </div>
    )
  }
}

/assets/react/MessagePane.js

'use strict';

import React from 'react';



export default class MessagePane extends React.Component {
  render () {
    if (!this.props.room) {
      return <div id="thread" />
    } else {
      return (
        <div id="thread">
          <h1>Room "{this.props.room.name}"</h1>
          <MessageList {...this.props}></MessageList>
          <MessageForm {...this.props} />
        </div>
      );
    }
  }
}


class MessageList extends React.Component {
  render () {
    if (!this.props.room.messages || !this.props.room.messages.length) {
      return <div />
    } else {
      var messageNodes = this.props.room.messages.map((message)=> {
        return ( <Message key={message.id} {...message} /> );
      });

      return (
        <div>
          <h2>MessageList</h2>
          <ul>
            {messageNodes}
          </ul>
        </div>
      );
    }
  }
}


class Message extends React.Component {
  render () {
    return (
      <li>
        <p>{this.props.content}</p>
      </li>
    )
  }
}


class MessageForm extends React.Component {
  _handleClick(e) {
    this.props.onAddMessage(React.findDOMNode(this.refs.inputText).value);
  }

  render () {
    return (
      <div>
        <input type="text" ref="inputText"></input>
        <button onClick={this._handleClick.bind(this)}>Send Message</button>
      </div>
    )
  }
}