「プロを目指すためのRuby入門」を1章から進めています

Kindle版を購入し、1章から進めています。

なぜ買ったか。

自分はRubyを仕事で使ったことないですが、いくつかサンプルアプリケーションを作成したことがあります

例えば下記のようなものです。

  • Cookpadでレシピを検索して返すLinebot

github.com

  • 1日のコミット回数をTwitterに投稿するアプリケーション github.com

これからRubyの仕事に就くことは今は考えていませんが、 自分の将来のキャリアパスを考えたときに一度は深めに触っておいた方がいいだろうと思い 今回の購入に至りました。

また、自分はAndroidエンジニアで基本的にフロントエンドの開発が主な業務ですが、サーバーチームとうまく連携して業務を進めていく上でサーバーサイドの知識もあったほうが、スムーズに仕事が進むのではないかとも考えています。

これから写経を進めていきます!!!

今日(2017/11/25)は連休3日目です。 連休1日目と2日目は十分遊び尽くしたので3日目と4日目はしっかりプログラムを書いていこうかなと思います。 随時このブログで書評を書いていきますのでよければ読者登録お願いいたします。

リハビリmemoのブログがすごく勉強になった話

Twitterでたまたま記事を見つけ拝見したところ、筋肉についてかなり勉強になるトピックがたくさんかかれていたので今回は個人的なメモとして残しておきます。

詳しくは下記のブログをご覧ください。このブログの運営者さんに感謝申し上げたいと思います

www.rehabilimemo.com

筋肉発達のメカニズム

  • 筋肉は分解量と合成量が釣り合った状態が今の筋肉の量
  • 筋トレ後48時間は筋肉の合成がよく働くのでこの間にたくさんのタンパク質を摂取する必要がある

筋トレ効果を最大にするタンパク質の摂取量

  • 一回のタンパク質の摂取量は年齢や体重、トレーニング強度によって変わるがベストな摂取量が存在している
筋トレに励む20代の体重70kgの男性を例にとると、通常の1食分のタンパク質摂取量は17〜20g程度で良いですが、ジムで全身性のレジスタンストレーニングを行った24時間〜48時間後までは1食分のタンパク質摂取量を通常の+10gである30g以上を摂取したほうが良いということになります。

筋トレ効果を最大にするタンパク質タイミング

  • 筋トレ前より筋トレ後の方が吸収がよくなるので飲むなら筋トレ後
  • トレーニング後から一時間以内がタンパク質摂取のゴールデンタイム
  • タンパク質は三時間毎の摂取がベスト
  • 3食きっちりプロテインを摂取することが大事

筋トレ効果を最大にする就寝前プロテイン

  • 就寝前には一般的な摂取量より多めにタンパク質を摂取することが大事
2008年から始まった就寝前のタンパク質摂取の研究により、高用量のタンパク質を摂取することによって、就寝時でも筋タンパク質の合成作用を刺激できることがわかりました。そしてトレーニングとともに就寝前のタンパク質摂取を長期に継続することで筋肉を効率的に増加させることが明らかになっているのです

就寝前にタンパク質を摂取する場合トレーニングは夕方行うと効果的

「夕方にトレーニングを行い、その後の夕食、就寝前、翌日の朝食、昼食に適切なタンパク質の摂取量を摂取することがトレーニング効果を最大化させる」

筋トレ効果を最大にする運動強度

  • トレーニング効果を最大にするのは「総負荷量(運動強度✖️運動回数)」
  • 初心者は低強度✖️低回数からはじめて徐々に運動回数を増やして総負荷をアップすること

最後に

他にもいろいろ勉強になることがいろいろ書かれていました。 筋トレをしている方なら必読だと思います。

www.rehabilimemo.com

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/にアクセスすると下記の画面が表示されます。

f:id:jesus9387:20171116153027p:plain

便利にするために

実行するたびに./node_modules/.bin/webpack-dev-serverとうつのはめんどくさいのでpackage.jsonを編集しておきましょう

package.json

"scripts": {
  "start": "webpack-dev-server"
}

するとnpm startで起動できるようになります。

よければ

ツイッターのフォローお願いします

twitter.com

寝ても寝ても疲れがとれない人のための スッキリした朝に変わる睡眠の本-書評

一冊読み終わったので書評として残しておきます

この本を読んだ理由

昔から自分は偏頭痛持ちでその原因が睡眠にも関係があるのではないかと思ったのと 一回の睡眠で最大限の休息を取るノウハウ的なものを習得したくてこの本を読み始めました。

疲れやすくなった原因は体力の衰え

年をとると「疲れやすくなった」と感じる方は多いのですが、これは加齢による体力の低下に加え、疲労回復物質FRの反応性が低くなっていくことも原因の1つです

正直自分はまだこれは感じていません。 (筋トレをしているからなのでは!??)

ですがだんだん衰えていくようです。

寝始めの3時間が大事

寝始めの3時間が重要なのであり、「午後10時~午前2時」という時間帯は関係ないのです。

10時から2時は「お肌のゴールデンタイム」のようなことが言われていますが、これはあまり関係ないと書かれていました。

寝始めの3時間で大量の回復や、成長因子のようなものがよく分泌されるようです。

音で起きることは人間にとってかなりのストレス

実は音で起きることはかなりストレスがかかるようです。

ですのでベストな起き方は[光で起きること]らしいです。

カーテンを少し開けておくことです。すると、朝になったらだんだんカーテンの隙間から光が入ってくるので、自然な目覚めが期待できます。

最近自分はこれを実践しています。 家の電気を消したらカーテンを開けて寝ています。

すると朝になるといい感じに光が入り目覚めがよくなっていることを実感しています。

また、

「朝起きたら、たっぷり光を浴びる」  これを続けることでスッキリ気持ちよく起きられるだけでなく、夜の快眠も手にいれることができるのです。

だそうです。

理由とかはこの本に書かれていますのでよければこちらからどうぞ

https://www.amazon.co.jp/gp/product/4569835880/ref=as_li_tl?ie=UTF8&tag=jesus01-22&camp=247&creative=1211&linkCode=as2&creativeASIN=4569835880&linkId=a96d48b31602587810c4c9c27b3eda3d

よろしければ

Twitterのフォローよろしくお願いします。

twitter.com

ElectronでHello world

久々に技術ブログを書いていきます。 (最近は書評ブログとかが多かったので、、、、)

babelやwebpack

「Electron 環境構築」とか「Electron Hello wold」とかで検索すると大体は単に画面にテキストを表示するのみで終わってしまっているのもが多いので今回はbabelwebpackなどもいれてトランスパイルできるようにしていきます。

自分は下記の本を購入して勉強しました。おすすめの本です。

ElectronでHello world

まずはnode.jsをインストールしてnpmコマンドが使えるようにしてください。 ググればいろいろでてきます。

その後、プロジェクトファイルを作成してnpm initを実行する

mkdir Helloworld
cd Helloworld
npm init -y

すと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 .

すると表示されます。

f:id:jesus9387:20171107222252p:plain

今回参考にした本

下記になります。 すごくおすすめ

よければ

よければツイッターの方フォローお願いします。

twitter.com

自分の時間を取り戻そう-書評

一冊読み終わったので書評として残しておきます。

感想

この本は一言で言うと、

「自分の時間は重要度のかなり高いもので、それを手に入れるために生産性を追求しましょう」

と言うことだと思います。

自分のいつもできるだけ仕事を終わらして帰るようにしているがどうしても忙しいときは残業してしまっている。

そんな自分の働き方を見直す必要があるな〜と考えさせられました。

長い時間を投入するのは本当にいいことなのか

この本の中で

「長い時間を投入することは本当に最良の問題解決方法なのか」

ということが述べられていました。

身につけたいことがあったら もっと最短で身につけることはできないか、 生産性高く学習することはできないか、

ということを常に考え続ける必要があります。

例えば自分の子供が 「長い時間をかけて勉強して成績があがった」 より、 「勉強時間はたいして変わってないけど成績がぐんとあがった」 のほうがよっぽど価値のあることだとも述べられていました。

やりたくないことこそダラダラ続けてしまう

やりたくないはダラダラやるとなんとかなってしまう。 でも生産性に目を向けた時にやりたくないことに我慢できなくなり本当にやりたいことが見つかるとも書かれていました。

これにはすごく共感しました。

最低賃金はもっとあげたほうがいい

このように書かれてた理由がすごく興味深かったです。

最低賃金の引き上げに反対し、海外からの労働者の受け入れに積極的な企業は安い労働力が豊富に入手できないとビジネスが成り立たない生産性の低い企業なのです。」

これには軽く衝撃でした。 たしかに短時間でかなりの利益をあげていればブラック企業は生まれない。 安い賃金で長時間働かせてなんとかしようとする企業はもうすでに生産性の低い企業としてレッテルを貼られているのと同じということと解釈しています。

なにか新しい技術や、ビジネスを目にした時に生産性という判断する

今ではITの力でいろんなことが自動化され、世の中をより便利にしてきました。

世の中で流行っているサービスのほとんどが「使うことで生産性があがるもの」です。

なので新しくビジネスを目にした時に「これは生産性があがるものなのか」という視点はとても大事だ思いました。

生産性が低いと思われる事例

生産性が低いと考えられる業務や、世の中の仕組みについていくつか書かれておりました。

例えば

  • アパレル店員
  • 選挙
  • 学校制度 etc

この辺りは理由もしっかり書かれていて面白かったのでよければ読んでみてください。

商品リンクはこちら

よければ

ツイッターの方フォローよろしくお願いします

twitter.com

Electron+React.jsで作るチャットアプリ

Electron + React.jsで作るチャットアプリケーション

自分はElectronの初心者です。 Electronで本を購入しチャットアプリを写経して自分の中で理解を深めるためにここに残しておきます。

  • 作成物

チャットアプリケーション

f:id:jesus9387:20171024182312g:plain

Github

github.com

  • 購入した本
  • 感想

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>
    );
  }

}
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>
    );
  }
}

実行

./node_modules/.bin/babel --out-dir tmp src
  • 実行
./node_modules/.bin/electron .

すると、実行できる

最後に

ただのメモ書きになってしましたが、 この本を勉強してかなり理解が深まったのでおすすめです。