「プロを目指すためのRuby入門」を1章から進めています
Kindle版を購入し、1章から進めています。
なぜ買ったか。
自分はRubyを仕事で使ったことないですが、いくつかサンプルアプリケーションを作成したことがあります
例えば下記のようなものです。
- Cookpadでレシピを検索して返すLinebot
- 1日のコミット回数をTwitterに投稿するアプリケーション github.com
これからRubyの仕事に就くことは今は考えていませんが、 自分の将来のキャリアパスを考えたときに一度は深めに触っておいた方がいいだろうと思い 今回の購入に至りました。
また、自分はAndroidエンジニアで基本的にフロントエンドの開発が主な業務ですが、サーバーチームとうまく連携して業務を進めていく上でサーバーサイドの知識もあったほうが、スムーズに仕事が進むのではないかとも考えています。
これから写経を進めていきます!!!
今日(2017/11/25)は連休3日目です。 連休1日目と2日目は十分遊び尽くしたので3日目と4日目はしっかりプログラムを書いていこうかなと思います。 随時このブログで書評を書いていきますのでよければ読者登録お願いいたします。
リハビリmemoのブログがすごく勉強になった話
Twitterでたまたま記事を見つけ拝見したところ、筋肉についてかなり勉強になるトピックがたくさんかかれていたので今回は個人的なメモとして残しておきます。
詳しくは下記のブログをご覧ください。このブログの運営者さんに感謝申し上げたいと思います
筋肉発達のメカニズム
- 筋肉は分解量と合成量が釣り合った状態が今の筋肉の量
- 筋トレ後48時間は筋肉の合成がよく働くのでこの間にたくさんのタンパク質を摂取する必要がある
筋トレ効果を最大にするタンパク質の摂取量
- 一回のタンパク質の摂取量は年齢や体重、トレーニング強度によって変わるがベストな摂取量が存在している
筋トレに励む20代の体重70kgの男性を例にとると、通常の1食分のタンパク質摂取量は17〜20g程度で良いですが、ジムで全身性のレジスタンストレーニングを行った24時間〜48時間後までは1食分のタンパク質摂取量を通常の+10gである30g以上を摂取したほうが良いということになります。
筋トレ効果を最大にするタンパク質タイミング
- 筋トレ前より筋トレ後の方が吸収がよくなるので飲むなら筋トレ後
- トレーニング後から一時間以内がタンパク質摂取のゴールデンタイム
- タンパク質は三時間毎の摂取がベスト
- 3食きっちりプロテインを摂取することが大事
筋トレ効果を最大にする就寝前プロテイン
- 就寝前には一般的な摂取量より多めにタンパク質を摂取することが大事
2008年から始まった就寝前のタンパク質摂取の研究により、高用量のタンパク質を摂取することによって、就寝時でも筋タンパク質の合成作用を刺激できることがわかりました。そしてトレーニングとともに就寝前のタンパク質摂取を長期に継続することで筋肉を効率的に増加させることが明らかになっているのです
就寝前にタンパク質を摂取する場合トレーニングは夕方行うと効果的
「夕方にトレーニングを行い、その後の夕食、就寝前、翌日の朝食、昼食に適切なタンパク質の摂取量を摂取することがトレーニング効果を最大化させる」
筋トレ効果を最大にする運動強度
- トレーニング効果を最大にするのは「総負荷量(運動強度✖️運動回数)」
- 初心者は低強度✖️低回数からはじめて徐々に運動回数を増やして総負荷をアップすること
最後に
他にもいろいろ勉強になることがいろいろ書かれていました。 筋トレをしている方なら必読だと思います。
React+babel+webpackで動かすまでのチュートリアル
React+webpack+babelを使ったチュートリアルを書いていきます。 Reactだけでサクッと「Hello world」を出して試したい方は 下記の公式サイトを参考にするとすぐに実装できます。
https://reactjs.org/docs/hello-world.html
準備
npm
コマンドが使えるように開発環境を準備してください。
その作業用ディレクトリを作成して移動する。
mkdir hellowold cd hellowold
必要なファイルのセットアップ
- プロジェクトの作成
npm init -y
- ディレクトリの作成
mkdir src dist
- babelのインストール
npm i -D babel-core babel-loader babel-preset-es2015 babel-preset-react
- babel設定ファイルの作成
.babelrc
{ "presets": [ "es2015", "react" ] }
- webpackのインストール
npm i -D webpack
- webpack.config.jsとdevelopment.jsに準備
webpack.config.js
require('babel-core/register'); module.exports = require('./development');
development.js
import path from 'path' const src = path.resolve(__dirname, 'src') const dist = path.resolve(__dirname, 'dist') export default { entry: src + '/index.jsx', output: { path: dist, filename: 'bundle.js' }, module: { loaders: [ { test: /\.jsx$/, exclude: /node_modules/, loader: 'babel-loader' } ] }, resolve: { extensions: ['.js', '.jsx'] }, plugins: [ ] }
- Reactパッケージのインストール
npm i -S react react-dom
- 最初に呼ばれる
index.js
の作成
src/index.jsx
import React from 'react'; import {render} from 'react-dom'; class App extends React.Component { render () { return <p> Hello React!</p>; } } render(<App/>, document.getElementById('app'));
- 実行してエラーがないか確認
./node_modules/.bin/webpack
webpack-dev-serverをインストール
webpack-dev-server
をインストールすることで自動コンパイル、自動リロード画面リロードができるようになるのでいれておきましょう。
npm i -D webpack-dev-server html-webpack-plugin
- development.jsを変数
//この行を追加
とかかれているものを追記する
import path from 'path' import HtmlWebpackPlugin from 'html-webpack-plugin'//この行を追加 const src = path.resolve(__dirname, 'src') const dist = path.resolve(__dirname, 'dist') export default { entry: src + '/index.jsx', output: { path: dist, filename: 'bundle.js' }, module: { loaders: [ { test: /\.jsx$/, exclude: /node_modules/, loader: 'babel-loader' } ] }, resolve: { extensions: ['.js', '.jsx'] }, plugins: [ //この行を追加 new HtmlWebpackPlugin({ template: src + '/index.html', filename: 'index.html' }) ] }
- index.htmlを作成
Reactで画面を表示するhtmlファイルを作成する
src/index.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>React Test</title> </head> <body> <div id="app" /> </body> </html>
- webpack-dev-serverを起動する
./node_modules/.bin/webpack-dev-server
$ ./node_modules/.bin/webpack-dev-server Project is running at http://localhost:8080/ webpack output is served from / Hash: 7e62cd70065fafed6306 Version: webpack 3.8.1 Time: 1099ms
- 起動する
ブラウザでhttp://localhost:8080/にアクセスすると下記の画面が表示されます。
便利にするために
実行するたびに./node_modules/.bin/webpack-dev-server
とうつのはめんどくさいのでpackage.json
を編集しておきましょう
package.json
"scripts": { "start": "webpack-dev-server" }
するとnpm start
で起動できるようになります。
よければ
ツイッターのフォローお願いします
寝ても寝ても疲れがとれない人のための スッキリした朝に変わる睡眠の本-書評
一冊読み終わったので書評として残しておきます
この本を読んだ理由
昔から自分は偏頭痛持ちでその原因が睡眠にも関係があるのではないかと思ったのと 一回の睡眠で最大限の休息を取るノウハウ的なものを習得したくてこの本を読み始めました。
疲れやすくなった原因は体力の衰え
年をとると「疲れやすくなった」と感じる方は多いのですが、これは加齢による体力の低下に加え、疲労回復物質FRの反応性が低くなっていくことも原因の1つです
正直自分はまだこれは感じていません。 (筋トレをしているからなのでは!??)
ですがだんだん衰えていくようです。
寝始めの3時間が大事
寝始めの3時間が重要なのであり、「午後10時~午前2時」という時間帯は関係ないのです。
10時から2時は「お肌のゴールデンタイム」のようなことが言われていますが、これはあまり関係ないと書かれていました。
寝始めの3時間で大量の回復や、成長因子のようなものがよく分泌されるようです。
音で起きることは人間にとってかなりのストレス
実は音で起きることはかなりストレスがかかるようです。
ですのでベストな起き方は[光で起きること]らしいです。
カーテンを少し開けておくことです。すると、朝になったらだんだんカーテンの隙間から光が入ってくるので、自然な目覚めが期待できます。
最近自分はこれを実践しています。 家の電気を消したらカーテンを開けて寝ています。
すると朝になるといい感じに光が入り目覚めがよくなっていることを実感しています。
また、
「朝起きたら、たっぷり光を浴びる」 これを続けることでスッキリ気持ちよく起きられるだけでなく、夜の快眠も手にいれることができるのです。
だそうです。
理由とかはこの本に書かれていますのでよければこちらからどうぞ
よろしければ
Twitterのフォローよろしくお願いします。
ElectronでHello world
久々に技術ブログを書いていきます。 (最近は書評ブログとかが多かったので、、、、)
babelやwebpack
「Electron 環境構築」とか「Electron Hello wold」とかで検索すると大体は単に画面にテキストを表示するのみで終わってしまっているのもが多いので今回はbabel
や webpack
などもいれてトランスパイルできるようにしていきます。
自分は下記の本を購入して勉強しました。おすすめの本です。
ElectronでHello world
まずはnode.jsをインストールしてnpm
コマンドが使えるようにしてください。
ググればいろいろでてきます。
その後、プロジェクトファイルを作成してnpm init
を実行する
mkdir Helloworld cd Helloworld npm init -y
すとJsonファイルが作成されます。
- package.json
{ "name": "Helloworld", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }
各種パッケージファイルのインストール
- Electronをインストール
npm install electron
- webpackのインストール
npm install webpack
- reactのインストール(今回はreactを使用します)
npm install react react-dom
- 画面描写に必要なファイルのインストール
npm install babel-core babel-loader babel-preset-es2015 babel-preset-react
- Windowを作成するファイル(最初に呼ばれるファイル)
src/main/index.jsを作成する(src と mainは各自ディレクトリを作成してください)
import { app } from "electron"; import createWindow from "./createWindow"; app.on("ready",() => { // Windowを生成する createWindow(); }); app.on("window-all-closed", () => { if (process.platform !== "darwin") { app.quit(); } }); app.on("activate", (_e, hasVisibleWindows) => { if (!hasVisibleWindows) { createWindow(); } });
- createWindow.jsを作成
electronでwindowを作成するクラスを作成する
src/main/createWindow.js
import { BrowserWindow } from 'electron'; let win; function createWindow() { win = new BrowserWindow(); win.loadURL(`file://${__dirname}/../../index.html`); win.on("close", () => { win = null; }); } export default createWindow;
- index.htmlを作成
createwindowしたファイルの上に表示するhtmlファイルです (これはプロジェクトディレクトリの直下においてください)
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>ブログみてね!</title> </head> <body> <div class="window"> <div id="app" class="window-content"></div> </div> <script>require("./dist/renderer/app.js")</script> </body> </html>
- app.jsxの作成
ここでHello worldします
src/renderer/app.jsx
import React from "react"; import { render } from "react-dom"; render(<div> Hello World </div>, document.getElementById("app"));
- webpackの設定ファイルを作成する
プロジェクトディレクトリの直下に下記のファイルを作成する
webpack.config.js
module.exports = { target: "electron", node:{ __dirname:false, __filename:false }, resolve:{ extensions: [".js",".jsx"] }, module:{ rules:[ { test: /\.jsx?$/, exclude:/node_modules/, loader: "babel-loader" }, { test: /.css$/, loaders:["style-loader","css-loader?modules"] } ] }, entry: { "main/index":"./src/main/index.js", "renderer/app": "./src/renderer/app.jsx" }, output: { filename: "dist/[name].js" }, devtool:"source-map" };
- .babelrcを作成
このアプリで使用するEcmascriptのバージョンを指定しています。
.babelrc
{ "presets":["es2015","react"] }
## ビルドする
ここまでで完了です。 ビルドしてみましょう。
./node_modules/.bin/webpack
package.jsを編集する
ここでビルド後のファイルを最初に読み込ませるように修正する必要があるのでpackage.jsを編集します
main~の箇所を下記のように編集してください
"main": "dist/main/index.js",
起動する
./node_modules/.bin/electron .
すると表示されます。
今回参考にした本
下記になります。 すごくおすすめ
よければ
よければツイッターの方フォローお願いします。
自分の時間を取り戻そう-書評
一冊読み終わったので書評として残しておきます。
感想
この本は一言で言うと、
「自分の時間は重要度のかなり高いもので、それを手に入れるために生産性を追求しましょう」
と言うことだと思います。
自分のいつもできるだけ仕事を終わらして帰るようにしているがどうしても忙しいときは残業してしまっている。
そんな自分の働き方を見直す必要があるな〜と考えさせられました。
長い時間を投入するのは本当にいいことなのか
この本の中で
「長い時間を投入することは本当に最良の問題解決方法なのか」
ということが述べられていました。
身につけたいことがあったら もっと最短で身につけることはできないか、 生産性高く学習することはできないか、
ということを常に考え続ける必要があります。
例えば自分の子供が 「長い時間をかけて勉強して成績があがった」 より、 「勉強時間はたいして変わってないけど成績がぐんとあがった」 のほうがよっぽど価値のあることだとも述べられていました。
やりたくないことこそダラダラ続けてしまう
やりたくないはダラダラやるとなんとかなってしまう。 でも生産性に目を向けた時にやりたくないことに我慢できなくなり本当にやりたいことが見つかるとも書かれていました。
これにはすごく共感しました。
最低賃金はもっとあげたほうがいい
このように書かれてた理由がすごく興味深かったです。
「最低賃金の引き上げに反対し、海外からの労働者の受け入れに積極的な企業は安い労働力が豊富に入手できないとビジネスが成り立たない生産性の低い企業なのです。」
これには軽く衝撃でした。 たしかに短時間でかなりの利益をあげていればブラック企業は生まれない。 安い賃金で長時間働かせてなんとかしようとする企業はもうすでに生産性の低い企業としてレッテルを貼られているのと同じということと解釈しています。
なにか新しい技術や、ビジネスを目にした時に生産性という判断する
今ではITの力でいろんなことが自動化され、世の中をより便利にしてきました。
世の中で流行っているサービスのほとんどが「使うことで生産性があがるもの」です。
なので新しくビジネスを目にした時に「これは生産性があがるものなのか」という視点はとても大事だ思いました。
生産性が低いと思われる事例
生産性が低いと考えられる業務や、世の中の仕組みについていくつか書かれておりました。
例えば
- アパレル店員
- 選挙
- 学校制度 etc
この辺りは理由もしっかり書かれていて面白かったのでよければ読んでみてください。
商品リンクはこちら
よければ
ツイッターの方フォローよろしくお願いします
Electron+React.jsで作るチャットアプリ
Electron + React.jsで作るチャットアプリケーション
自分はElectronの初心者です。 Electronで本を購入しチャットアプリを写経して自分の中で理解を深めるためにここに残しておきます。
- 作成物
チャットアプリケーション
- 購入した本
- 感想
Electronについて概要からしっかり学べた
## Electron本体をインストール
- 作業用のディレクトリを作成
mkdir electron_chat
- 作業用のディレクトリを移動
cd electron_chat
- プロジェクト作成
npm init -y
(npmが使えなかった場合はインストールしてくる)
- Electronをインストール
npm install electron@1.6.1 --save-dev
- photonKitのインストール
photonKitとはmacOSネイティブアプリケーションのようなUIを簡単に実装できるFrameworkです。
npm install connors/photon --save-dev
- Reactのインストール
npm instal react@15.4.2 react-dom@15.4.2 react-router@3.0.0 --save
- babel関連モジュールのインストール
npm install babel-cli@6.18.0 babel-preset-es2015@6.18.0 babel-preset-react@6.16.0 --save-dev
実装
- index.js
import { app } from "electron"; import createWindow from "./createWindow"; import setAppMenu from "./setAppMenu" app.on("ready",() => { //アプリが起動したとき // 上でimportしたcreateWindowクラスのcreateWindow()メソッドを呼ぶ createWindow(); }); app.on("window-all-closed", () => { //mac以外の場合はウインドウを閉じた時にアプリを終了する if (process.platform !== "darwin") { app.quit(); } }); app.on("activate", (_e, hasVisibleWindows) => { if (!hasVisibleWindows) { createWindow(); } });
createWindow.js
import { BrowserWindow } from 'electron'; let win; function createWindow() { win = new BrowserWindow(); //BrowserWindowクラスを使ってHTMLファイルを読み込み画面に表示 win.loadURL(`file://${__dirname}/../../index.html`); win.on("close", () => { win = null; }); } export default createWindow;
- index.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Electron Chat</title> <link rel="stylesheet" href="node_modules/photon/dist/css/photon.css"/> </head> <body> <div class="window"> <div id="app" class="window-content"></div> </div> <script>require("./.tmp/renderer/app.js")</script> </body> </html>
- app.jsx
Railsでいうrouteみたいなファイル。この中にFirebaseの設定も書き込む
import React from "react"; import {render} from "react-dom"; import { Router, Route, hashHistory } from "react-router"; import Login from "./Login"; import Signup from "./Signup"; import Rooms from "./Rooms"; import Room from "./Room"; import firebase from "firebase/firebase-browser" // Routingの定義 const appRouting = ( <Router history={hashHistory}> //親のpathから繋げていく <Route path="/"> //例えば "/login"を設定するとLoginクラスが呼ばれる <Route path="login" component={Login} /> <Route path="signup" component={Signup} /> <Route path="rooms" component={Rooms} > <Route path=":roomId" component={Room} /> </Route> </Route> </Router> ); //Routingの初期化 if (!location.hash.length) { location.hash = "#/login"; } // Firebaseの初期化 //ここはFirebaseからこぴぺ const config = { apiKey: "*******************", authDomain: "*******************", databaseURL: "*******************", projectId: "*******************", storageBucket: "", messagingSenderId: "*******************" }; firebase.initializeApp(config); // Applicationの描画 render(appRouting, document.getElementById("app"));
- .babellc
Reactをjavascriptに変換するためのファイル
{ "preset": ["es2015,"react] }
- Login.js
ログイン画面の実装
import React from "react"; import { Link, hashHistory } from "react-router"; import Errors from "./Errors"; import firebase from "firebase/firebase-browser"; const FORM_STYLE = { margin:"0 auto", padding:30 }; const SIGNUP_LINK_STYLE = { display:"inline-block", marginLeft: 10 }; export default class Login extends React.Component { constructor(props) { super(props); //props:親から渡される情報 //state:自Componentの状態 this.state = { email: localStorage.userEmail || "", password: localStorage.userPassword || "", errors:[], }; //それぞれのメソッドをbindする //bindすることでフロントの状態変化を検知できる this.handleOnChangeEmail = this.handleOnChangeEmail.bind(this); this.handleOnChangePassword = this.handleOnChangePassword.bind(this); this.handleOnSubmit = this.handleOnSubmit.bind(this); } // 下のinputの状態が変化したら呼ばれる handleOnChangeEmail(e){ this.setState({ email: e.target.value}); } // 下のinputの状態が変化したら呼ばれる handleOnChangePassword(e){ this.setState({ password: e.target.value }); } //ログイン処理 handleOnSubmit(e){ // 二つのinputタグの状態を取得する const { email, password } = this.state; const errors = []; let isValid = true; e.preventDefault(); if (!email.length) { //emailに何もデータが入っていない場合 isValid = false; errors.push("Email cannot blank."); } if (!password.length) { //passwordに何もデータが入っていない場合 isValid = false; errors.push("Password cannot be blank."); } if (!isValid) { //必須入力チェックに該当した場合はエラーを表示する this.setState({ errors }); return; } //Firebaseのログイン処理 //この辺はリファレンスを見る firebase.auth().signInWithEmailAndPassword(email,password).then(() => { //次回ログインし簡略化のため、localStorageに値を保存 localStorage.userEmail = email; localStorage.userPassword = password; //チャットルーム一覧画面へ遷移 hashHistory.push("/rooms"); }).catch(() => { // Firebaseでログインエラーになった場合 this.setState({errors: ["Incorrect email or password."] }) }); } //フロントのレンダリング render(){ return ( <form style={FORM_STYLE} onSubmit={this.handleOnSubmit}> <Errors errorMessages={this.state.errors} /> <div className="form-group"> <label>Email address</label> <input type="email" className="form-control" placeholder="email" onChange={this.handleOnChangeEmail} value={this.state.email} /> </div> <div className="form-group"> <label>Password</label> <input type="password" className="form-control" placeholder="password" onChange={this.handleOnChangePassword} value={this.state.password} /> </div> <div className="form-group"> <button className="btn btn-large btn-default">Login</button> <div style={SIGNUP_LINK_STYLE}> <Link to="/signup">create new account</Link> </div> </div> </form> ); } }
- signup.js
サインアップページ
import React from "react"; import { Link, hashHistory } from "react-router"; import Errors from "./Errors"; import firebase from "firebase/firebase-browser"; const SIGNUP_FORM_STYLE = { margin: "0 auto", padding: 30 }; const CANCEL_BUTTON_STYLE = { marginLeft: 10 }; export default class Signup extends React.Component{ constructor(props){ super(props); //状態の初期化 this.state = { email:"", password:"", name:"", photoURL:"", errors:[] }; //各種bindする this.handleOnChangeEmail = this.handleOnChangeEmail.bind(this); this.handleOnChangePassword = this.handleOnChangePassword.bind(this); this.handleOnChangeName = this.handleOnChangeName.bind(this); this.handleOnChangePhotoURL = this.handleOnChangePhotoURL.bind(this); this.handleOnChangeSubmit = this.handleOnChangeSubmit.bind(this); } handleOnChangeEmail(e){ this.setState({ email: e.target.value}); } handleOnChangePassword(e){ this.setState({ password:e.target.value }); } handleOnChangeName(e){ this.setState({ name: e.target.value}); } handleOnChangePhotoURL(e){ this.setState({photoURL: e.target.value }); } handleOnChangeSubmit(e){ const {email, password, name,photoURL } = this.state; const errors = []; let isValid = true; e.preventDefault(); if (!email.length) { isValid = false; errors.push("Email address cannt be blank."); } if (!password.length) { isValid = false; errors.push("Password cannot be blank"); } if (!name.length) { isValid = false; errors.push("Name cannot be blank."); } if (!isValid) { this.setState({errors}); return; } // Firebaseの新規アカウント作成処理 firebase.auth().createUserWithEmailAndPassword(email,password).then(newUser => { //ユーザー情報を更新する return newUser.updateProfile({ displayName:name,photoURL }); }).then(() => { //チャットルーム一覧画面へ遷移 hashHistory.push("/rooms"); }).catch(err => { // Firebaseでエラーになった場合 this.setState({errors: [err.message] }) }); } render(){ return ( <form style={SIGNUP_FORM_STYLE} onSubmit={this.handleOnChangeSubmit}> <Errors errorMessages={this.state.errors} /> <div className="form-group"> <label>Email address *</label> <input type="email" className="form-control" placeholder="email" value={this.state.email} onChange={this.handleOnChangeEmail} /> </div> <div className="form-group"> <label>Password *</label> <input type="password" className="form-control" placeholder="password" value={this.state.password} onChange={this.handleOnChangePassword} /> </div> <div className="form-group"> <label>User name *</label> <input type="text" className="form-control" placeholder="user_name" value={this.state.name} onChange={this.handleOnChangeName} /> </div> <div className="form-group"> <label>Photo URL</label> <input type="text" className="form-control" placeholder="photo url" value={this.state.photoURL} onChange={this.handleOnChangePhotoURL} /> </div> <div className="form-group"> <button className="btn btn-large btn-primary">Create new account</button> <Link to="/login"> <button type="button" style={CANCEL_BUTTON_STYLE} className="btn btn-large btn-default" > cencel </button> </Link> </div> </form> ); } }
- Rooms.jsx
左ペインにRoom一覧を表示し、右ペインにはチャットルームを表示する
import React from "react"; import { hashHistory } from "react-router"; import RoomItem from "./RoomItem"; import firebase from "firebase/firebase-browser"; const ICON_CHAT_STYLE = { fontSize: 120, color:"#DDD" }; const FORM_STYLE = { display: "flex" }; const BUTTON_STYLE = { marginLeft: 10 }; export default class Rooms extends React.Component { constructor(props){ super(props); this.state = { roomName: "", rooms:[] }; // Firebaseデータベースを初期化 this.db = firebase.database(); this.handleOnChangeRoomName = this.handleOnChangeRoomName.bind(this); this.handleOnSubmit = this.handleOnSubmit.bind(this); } componentDidMount(){ //コンポーメントの初期化時にチャットルーム一覧を取得する this.fetchRooms(); } handleOnChangeRoomName(e){ this.setState({ roomName: e.target.value }); } handleOnSubmit(e){ const { roomName } = this.state; e.preventDefault(); if (!roomName.length) { return; } //Firebaseデータベースに新規チャットルームのデータ作成 const newRoomRef = this.db.ref("/chatrooms").push(); const newRoom = { description: roomName }; //作成したチャットルームのdescriptionを更新する newRoomRef.update(newRoom).then(() => { //状態を再初期化する this.setState({ roomName:"" }) //チャットルーム一覧を再取得 return this.fetchRooms().then(() => { //右ペインを作成した詳細画面に遷移させる hashHistory.push(`/rooms/${newRoomRef.key}`); }); }); } //チャットルーム一覧の取得 fetchRooms(){ //Firebaseデータベースからチャットルームを20件取得 return this.db.ref("/chatrooms").limitToLast(20).once("value").then(snapshot => { const rooms = []; snapshot.forEach(item => { //データベースから取得したデータオブジェクトとして取り出す rooms.push(Object.assign({key:item.key}, item.val())); }); //取得したオブジェクトの配列をコンポーメントのstateにセット this.setState({rooms}); }); } //左ペイン(チャットルーム一覧)の描写処理 renderRoomList(){ const { roomId } = this.props.params; const { rooms, roomName } = this.state; return ( <div className="list-group"> //ルーム一覧をfetchRoomsから取得したデータ分だけ表示する //selectしたらRoomItemにroomIdを設定する // RoomItemタグを使っているといことはRoomItemクラスを呼んでいる {rooms.map(r => <RoomItem room={r} key={r.key} selected={r.key === roomId} /> )} //新しいチャットルームを作成するためのinputエリア <div className="list-group-header"> <form style={FORM_STYLE} onSubmit={this.handleOnSubmit}> <input type="text" className="form-control" placeholder="New room" onChange={this.handleOnChangeRoomName} value={roomName} /> <button className="btn btn-default" style={BUTTON_STYLE}> <span className="icon icon-plus" /> </button> </form> </div> </div> ); } //右ペイン(チャットルーム詳細)の描写処理 renderRoom(){ if (this.props.children) { // propsにデータが設定されている = Roomが設定されている return this.props.children; } else { return ( <div className="text-center"> <div style={ICON_CHAT_STYLE}> <span className="icon icon-chat" /> </div> <p> Join a chat room from the sidebar or create your chat room. </p> </div> ); } } render(){ return ( <div className="pane-group"> //左ペイン <div className="pane-sm sidebar">{this.renderRoomList()}</div> //右ペイン <div className="pane">{this.renderRoom()}</div> </div> ); } }
- RoomItem.jsx
Room一覧ののひとつひとつのアイテム
import React from "react" import { Link } from "react-router" const LINK_STYLE = { color:"inherit", textDecoration:"none" }; export default function RoomItem(props) { //propsからアイテムひとつひとつを設定する const { selected } = props; const { description, key } = props.room; return ( <div className={selected ? "list-group-item selected":'list-group-item'}> //`/rooms/${key}`の設定でroom.jsxが呼ばれて設定される <Link to={`/rooms/${key}`} style={LINK_STYLE}> <div className="media-body"> <strong>{description}</strong> </div> </Link> </div> ); }
- Room.jsx
選択されているチャットルームでチャット処理を実装する
import React from "react"; import Message from "./Message"; import NewMessage from "./NewMessage"; import firebase from "firebase/firebase-browser"; const ROOM_STYLE = { padding: "10px 30px" }; export default class Room extends React.Component { constructor(props){ super(props); this.state = { description: "", messages:[], }; this.db = firebase.database(); this.handleOnMessagePost = this.handleOnMessagePost.bind(this); } componentDidMount(){ const { roomId } = this.props.params; //コンポーメントの初期化時にチャットルームの詳細情報を取得する this.fetchRoom(roomId); } componentWillReceiveProps(nextProps){ const { roomId } = nextProps.params; if (roomId === this.props.params.roomId) { //チャットルームのIDいん変更がなければなにもしない return; } if (this.stream) { //メッセージの監視解除 this.stream.off(); } // stateの再初期化 this.setState({ messages:[] }); //チャットルーム詳細の再取得 this.fetchRoom(roomId); } componentDidUpdate(){ setTimeout(() => { //画面下端へスクロール this.room.parentNode.scrollTop = this.room.parentNode.scrollHight; }, 0); } componentWillUnmount(){ if (this.stream) { //メッセージ監視を解除 this.stream.off(); } } //メッセージ投稿処理 handleOnMessagePost(message){ const newItemRef = this.fbChatRoomRef.child("messages").push(); ///Firebaseにログインしているユーザーを投稿ユーザーとして利用 this.user = this.user || firebase.auth().currentUser; return newItemRef.update({ writtenBy:{ uid:this.user.uid, displayName: this.user.displayName, photoURL:this.user.photoURL, }, time:Date.now(), text:message, }); } fetchRoom(roomId){ //Firebaseデータベースからチャットルーム詳細データの参照を取得 this.fbChatRoomRef = this.db.ref("/chatrooms/" + roomId); this.fbChatRoomRef.once("value").then(snapshot => { const { description } = snapshot.val(); this.setState({ description }); window.document.title = description; }); this.stream = this.fbChatRoomRef.child("messages").limitToLast(10); //チャットルームのメッサージ追加を監視 this.stream.on("child_added",item => { const { messages } = this.state || []; // 追加されたメッセージをstateにセット messages.push(Object.assign({key:item.key},item.val())); this.setState({ messages }); }); } render(){ const { messages } = this.state; return ( <div style={ROOM_STYLE} ref={room => this.room = room} > <div className="list-group"> //fetchRoomで取得したデータ分だけ画面に表示 {messages.map(m => <Message key={m.key} message={m} />)} </div> <NewMessage onMessagePost={this.handleOnMessagePost} /> </div> ); } }
- Message.jsx
1メッセージのオブジェクト
import React from "react"; import Avatar from "./Avatar"; const MEDIA_BODY_STYLE = { color: "#888", fontSize:".9em" }; const TIME_STYLE = { marginLeft: 5 }; const TEXT_STYLE = { whiteSpace:"normal", wordBreak:"break-word" }; export default function Message(props){ const { text, time, writtenBy } = props.message; const localeString = new Date(time).toLocaleString(); return ( <div className="list-group-item"> <div className="media-object pull-left"> <Avatar user={writtenBy} /> </div> <div className="media-body"> <div style={MEDIA_BODY_STYLE}> <span>{writtenBy.displayName}</span> <span style={TIME_STYLE}>{localeString}</span> </div> <p style={TEXT_STYLE}> {text} </p> </div> </div> ); }
- NewMessage.jsx
投稿するinputタグ
import React from "react" const FORM_STYLE = { display: "flex" }; const BUTTON_STYLE = { marginLeft: 10 }; export default class NewMessage extends React.Component{ constructor(props){ super(props); this.state = {message: ""}; this.handleOnChange = this.handleOnChange.bind(this); this.handleOnSubmit = this.handleOnSubmit.bind(this); } handleOnChange(e){ this.setState({message:e.target.value}); } handleOnSubmit(e){ const { onMessagePost } = this.props; if (!onMessagePost || !this.state.message.length) { return; } onMessagePost(this.state.message); this.state({ message: ""}); e.preventDefault(); } render(){ return( <form style={FORM_STYLE} onSubmit={this.handleOnSubmit}> <input type="text" className="form-control" onChange={this.handleOnChange} value={this.state.message} /> <button className="btn btn-large btn-primary" style={BUTTON_STYLE}>POST</button> </form> ); } }
- Avatar.jsx
import React from "react" const AVATAR_STYLE = { width:32, textAlign:"center", fontSize:24 }; export default function Avatar(props){ const { photoURL } = props.user; if(photoURL){ // photoURLURLが設定されている場合img要素を表示 return <img className="img-rounded" src={photoURL} style={AVATAR_STYLE} />; } else { //photoURLが設定されていない場合代替えとしてiconを表示 return ( <div style={AVATAR_STYLE}> <span className="icon icon-user" /> </div> ); } }
実行
- Reactをトランスコンパイル
./node_modules/.bin/babel --out-dir tmp src
- 実行
./node_modules/.bin/electron .
すると、実行できる
最後に
ただのメモ書きになってしましたが、 この本を勉強してかなり理解が深まったのでおすすめです。