はじめに
勉強していたReact
をどのくらい理解しているのか、どんな知識が不足しているのかを知るためにチャットシステムの構築に挑戦しました。
丁寧な解説を心がけて記事を執筆していきます。
わからない点、理解しづらい所があればコメントでの質問をお願いします。
指摘も大歓迎です。
現在の状況
ReduxやFirebaseといった機能を追加しようとしているため、十分に稼働していない可能性があります。
参考程度に捉えるのがオススメです。
-
URL: Reactチャット
-
Github: kaibara/React-chat
参考図書
このチャットは以下の本を参考にしながら作成しました。
いまどきのJSプログラマーのための Node.jsとReactアプリケーション開発テクニック
環境設定
ファイル構成
このチャットは以下のファイル構成で稼働しています。
|
|ーー src //作業用ディレクトリ
| |ーー index.js //componentsでのファイル、componentがここにまとめられる
| |ーー components
| |ーー Form.js //チャットのFormを作成するファイル
| |ーー App.js //チャットのlogを管理するファイル
|
|ーー public //出力用ディレクトリ
| |ーー index.html //bundle.jsを読み込み、サーバーに読み込まれるファイル
| |ーー bundle.js //srcのファイルがここにまとめられ、index.htmlに適用される
|
|ーー node_module //このプロジェクトで使用されるmoduleがまとめられているディレクトリ(操作しない)
|ーー server.js //Node.jsにおけるサーバーの設定をするファイル
|ーー package.json //このプロジェクトを管理するためのファイル
|ーー package-lock.json //package.jsonの状態を保存するファイル(操作しない)
|ーー webpack.configjson //webpackで最適化する際の設定が記載されたファイル
|ーー README.md //Githubのリポジトリにアクセスした際、そのリポジトリの説明などを行うファイル
|ーー .gitgnore //端末で操作するgitのコマンドなどを管理するファイル(基本的に操作しない)
Github
まずはGithubで新しいリポジトリを作成しましょう。
ファイルの状態を管理してくれるだけじゃなく、以前の状態にファイルを戻すことができるため、かなり重宝します。
リポジトリを作成したら次へ進んでください。
moduleのインストール
今回はユーザー同士がやり取りできるよう、サーバーが必要です。
JavaScript
だけでサーバーを動かせるように、Node.js
を使用します。
また、多くのmoduleを使用、管理するためにnpm
も利用します。
Node.js
をインストールすればnpm
もインストールされるため、次のURLからそれぞれのOSに合った方法でインストールしてください。
Download | Node.js
参考資料: 手順を分かりやすく解説!Node.jsのインストール方法 | TechAcademyマガジン
Node.js
、npm
がインストールできたら、Githubからリポジトリをコピーし、次のようにnpm
からモジュールをインストールしましょう。
$ git clone {Githubで作成したリポジトリのSSH}
// 作成したリポジトリをコピーしてローカルで操作できるようにする。
$ cd {Githubで作成したリポジトリの名前}
$ git checkout -b {ブランチ名}
$ npm init
//それぞれの内容に合わせてキーを打つ or Enter
//その内容に合わせてnpmがプロジェクトを管理するpackage.jsonを作成される。
$ npm install --save express socketio socket.io-client react react-dom
//開発、実行時共に必要なmodule
$ npm install --save-dev webpack webpack-cli babel-loader babel-core babel-preset-es2015 babel-preset-react
//開発時のみに必要なmodule
各moduleの解説
-
express
Node.js
で使っている人が最も多いWebフレームワークです。
API(アプリケーションプログラミングインターフェース)があることで、サーバーとのデータの送受信が可能となります。 -
socketio,socket.io-client
サーバーとクライアントの双方向通信を効率的に行うWebSocketを実装するためのmoduleです。 -
react,react-dom
react
でアプリケーションを作成するファイルはreact
をimport
する必要があります。
react-dom
によってhtmlなどの特定の部分にreact
のプログラムを適用させることができるようになるのです。 -
webpack,webpack-cli
作成したファイルをjs
やhtml
など言語で分けた後にまとめて、最適化してくれます。 -
babel-loader,babel-core,babel-preset-es2015,babel-preset-react
開発者が使用している言語とブラウザーで使用される言語は同じであってもバージョンが異なることがあります。
そのままWeb上に公開してしまっては、最新のバージョンでしか使用できないコードがあった場合、ブラウザーではコードを認識できません。
それを防ぐためのトランスパイラと呼ばれるツール達がこのmoduleです。
package.jsonの設定
npm
でモジュールのインストールが完了したら、プロジェクトの管理をするpackage.json
を確認してください。
{
"name": "***",
"version": "1.0.0",
"description": "",
"main": "server.js",
//$ npm init で指定した内容が入力される
"scripts": {
"start": "node server.js",
//server.jsの内容に基づいてnode.jsのサーバーが起動する
"build": "webpack",
//webpackによって読み込んだファイルを最適化する
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
//$ npm install --save でインストールした開発、実行で使用するmodule達
},
"devDependencies": {
//$ npm install --save-dev でインストールした開発のみで使用するmodule達
}
}
ここでは"scripts"
を見てください。
そこに書かれている左側の単語を$ npm (左側の単語)
と打ち込むことで、右側のコマンドが実行されます。
webpack.config.jsの設定
上にもある$ npm run build
、$ webpack
が行われる際、このファイルを元に作業したファイルの最適化が行われるのです。
どのような内容があるかをコメントアウトで説明していきます。
const path = require('path')
module.exports = {
entry: path.join(__dirname, 'src/index.js'),
//root/src/index.jsを読み込む
output: {
path: path.join(__dirname, 'public'),
filename: 'bundle.js'
},
//root/public/ にbundle.jsとしてentryで読み込んだファイルを最適化し出力する
devtool: 'inline-source-map',
//chromeの開発者ツールで最適化されるファイル達をその前の状態で確認できるようにする
module: {
rules: [
{
test: /.js$/,
loader: 'babel-loader',
options: {
presets: ['es2015','react']
}
}
]
}
//babelなどトランスパイルをするmoduleを最適化する際のルールの設定
}
サーバーの設定
このチャットでは、httpサーバーとWebsocketサーバーが存在しています。
流れを簡潔に解説します。
ユーザーがブラウザで指定されたURLに接続した時にhttpサーバと接続します。
その後、httpサーバーからチャットを構成するファイルやcomponentが渡されるのです。
それと同時にWebSocketのクライアントアプリがユーザーに引き渡されます。
そうすることで、WebSocketによる双方向通信が可能となるのです。
では、Node.js
で起動するサーバーの設定を行うserver.js
を作成していきましょう。
const express = require('express')
const app = express()
const server = require('http').createServer(app)
//httpサーバーの作成
//const server = createServer(app)でもサーバーは作成できるが、expressのサーバーを動かすとなると多くのmoduleが必要になる。
//httpだけであれば必要最低限の構成でサーバーを起動できる
const portNumber = 3005
//番号はなんでも良いが、他で使用している番号と重ならないように注意
server.listen(portNumber, () => {
console.log('起動しました', 'http://localhost' + portNumber)
})
//サーバーの起動時、端末にポート番号を表示する。
//そうすることで、コピー&ペーストによって簡単にブラウザからチャットに接続できる。
app.use('/public', express.static('./public'))
//クライアントにアクセスさせたい情報をapp.useに格納している
app.get('/',(req,res) => {
res.redirect(302, '/public')
})
//クライアントからの要求があったときに一時的にpublicの内容を渡す
const socketio = require('socket.io')
const io = socketio.listen(server)
//WebSocketサーバーの設定
io.on('connection',(socket) => {
//ユーザーが接続してきた時の処理
console.log('Acces to User:', socket.client.id)
socket.on('chatMessage',(msg) => {
//メッセージ受信時の処理
console.log('message',msg)
io.emit('chatMessage',msg)
//全てのユーザーにメッセージを送信
})
})
React編
続いて、チャットを構成するcomponentを解説していきます。
chatForm.js
このチャットでは名前
とメッセージ
を送信できるようにします。
それぞれのフォームを構成しているのがこのForm.js
です。
各タグのid
やclassName
はcss
を適用させやすいようにしているだけです。
省略しても構いません。
import React,{Component} from 'react'
import socketio from 'socket.io-client'
const socket = socketio.connect('http://localhost:3005')
class Form extends Component {
constructor(props){
super(props)
this.state = {
name: '',
message: ''
//このcomponentで扱うnameとmessageの初期値を設定する。
}
}
nameChanged(e){
this.setState({name: e.target.value})
}
//このイベントの発生時、this.state.nameにvalueの値が入る
messageChanged(e){
this.setState({message: e.target.value})
}
//このイベントの発生時、this.state.messageにvalueの値が入る
send(){
socket.emit('chatMessage',{
name: this.state.name,
message: this.state.message
})
this.setState({message: ''})
}
//このイベント発生時、socket.io-clientがlocahostにnameとmessageの値が入ったchatMessageを全てのユーザーに送信する。
//その後、messageの値だけを初期値に戻す。
render(){
return(
<div id='Form'>
<div className='Name'>
名前:
<br />
<input value={this.state.name} onChange={e => this.nameChanged(e)} />
//名前フォーム内に何か打ち込まれたらthis.state.nameに打ち込まれたものが入り、this.nameChangedイベントが発生する
</div>
<br />
<div className='Message'>
メッセージ:
<br />
<input value={this.state.message} onChange={e => this.messageChanged(e)} />
//メッセージフォーム内に何か打ち込まれたらthis.state.nameに打ち込まれたものが入り、this.messageChangedイベントが発生する
</div>
<button className='send' onClick={e => this.send()}>送信</button>
//ボタンを押されたらsendが発生する
</div>
)
}
}
export default Form
App.js
このcomponentではForm.js
から送られた値をlogとして管理するための設定を行っています。
import React,{Component} from 'react'
import socketio from 'socket.io-client'
import Form from './Form'
const socket = socketio.connect('http://localhost:3005')
class App extends Component {
constructor(props){
super(props)
this.state = {
logs: []
}
//このcomponentで扱う配列logsの初期値を設定する
}
componentDidMount(){
//このコンポーネントがDOMによって読み込まれた後の処理を設定する
socket.on('chatMessage',(obj) => {
//WebSocketサーバーからchatMessageを受け取った際の処理
const logs2 = this.state.logs
//logs2に今までのlogを格納する
obj.key = 'key_' + (this.state.logs.length + 1)
//メッセージ毎に独自のキーを設定して判別できるようにする
console.log(obj)
//consolelogにobj.key、name、messageを表示する
logs2.unshift(obj)
//配列の一番最初に最新のメッセージを入れる。
//そうすることで新しいメッセージほど上に表示されるようになる
this.setState({logs: logs2})
//最新のkey、name、messageが入ったlogs2をlogsに入れる。
})
}
render(){
const messages = this.state.logs.map(e => (
<div key={e.key}>
<span>{e.name}</span>
<span>: {e.message}</span>
<p />
</div>
))
//ログの設定。今までのname、messageをkeyごとに表示する
return(
<div>
<h1 id='title'>Reactチャット</h1>
<ChatForm />
<div id='log'>{messages}</div>
</div>
)
}
}
export default App
index.js
App.js
を読み込み、DOMに書き込みます。
そして、webpackで最適化が行われる際に、このファイルが読み込まれるのです。
import React from 'react'
import ReactDOM from 'react-dom'
import App from './components/App'
ReactDOM.render(
<ChatApp />,
document.getElementById('root')
)
起動編
public/index.html
src/
ディレクトリでチャットの設定が完了したら、public/index.html
のファイルを編集します。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Rectチャット</title>
/*chromeなどのタブに表示する名前の設定*/
</head>
<body>
<div id="root"></div>
<script src="/public/bundle.js"></script>
/*webapckによって作成されたbundle.jsが読み込まれる。*/
/*つまり、src/以下のファイルで書いたcomponentが反映される*/
</body>
</html>
npmでサーバーを起動する
以上で、ファイルにコードを書くのは終了です。
あとは、端末での操作を行い、サーバーを起動しましょう。
行う操作は次の2つです。
1つ目は$ npm run build
package.json
のscripts
でbuild: start
と定義しているため、このコマンドを打つとwebpack
によってsrc/index.js
が最適化されて、public/bundle.js
に出力されます。
2つ目は$ npm start
1つ目と同様、package.json
のscripts
でstart: node server.js
と定義しているため、このコマンドを打つとNode.js
がserver.js
を読み込み、その設定基づいてサーバーを起動します。
端末を開くと起動しました: http://localhost:3005
が表示されます。
ブラウザでそのURLに接続すると、チャットが表示されます。
もし、表示されていない場合はエラーが発生しているため、適宜修正していってください。
まとめ
以上、Reactでの作成手順を解説しました。
このチャットでは更新するたびにログが削除されます。
更新しても今までのログを表示するには、jQueryなどでバックエンドにデータを保管するといった方法が考えられます。
この記事を元にチャットの型、フロント部分を作成し、後は自分で作りたいチャットにできるよう手を加えたり、修正されたりしてみてはいかがでしょうか。
参考資料
webpack.config.js
のdevtoolsについて- React.jsでビルドされる前のコードをブラウザで確認する
webpack.config.js
のmoduleについて- webpackチートシート
server.js
のrequire('http')
について - node.jsでrequire('http');する理由