チャットシステムの完成
概要
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>
)
}
}