Works by

プログラミング等IT技術関連でメモする

【React】redux-loggerでActionやStateのログを出力する

redux-logger

ReduxではRedux Middelwareという様々な機能拡張が有志の手によって提供されています(プラグインのようなものです)。これから紹介するredux-loggerもRedux Middlewareの一つであり、これを使えばRedux上のActionディスパッチやStateの変化を容易にロギングすることができます。

インストールも簡単で、他のツール同様npmを使うだけです。

$ npm install --save redux-logger

Usage

既存のアプリケーションに挿入する前提で説明します。

まずはreduxからapplyMiddlewareと、redux-loggerからloggerをインポートします(createStoreは後のStore作成用)。

import { createStore, applyMiddleware } from 'redux';
import logger from 'redux-logger';

次に、Store生成時に呼ばれるcreateStoreの第二引数にapplyMiddleware(logger)を追加します。applyMiddlewareはRedux Middlewareを有効化するのに必要であり、任意個数のミドルウェアを引数に取ることができます。今回はredux-loggerのみ必要なので、loggerを引数に渡しています。

const store = createStore(tasksReducer, applyMiddleware(logger));

これでOKです。アプリケーションを起動し、Actionが発生するたびにログが表示されていることがわかります。

f:id:rennnosukesann:20180520142711p:plain

拡張

redux-loggerは今のままでも様々な情報を取得できますが、より詳細なカスタマイズを行うことも可能です。下記リポジトリに詳細なUsage が記載されているので、もしもっとloggerをいじって見たい人は参考にするといいかもしれません。

github.com

【React】React+JSXを用いてUIコンポーネントを作成する①

前回の記事でJSXについて書いたので、今回は簡単なUIコンポーネント作成の流れをメモします。

前準備

create-react-appをインストールし、Reactプロジェクトを作成します(アプリの立ち上げも行っておきます)。

$ brew install node
$ npm install -g create-react-app
$ mkdir YourReactProject; cd YourReactProjectDir
$ create-react-app my-app
$ cd my-app
$ npm start 

UIコンポーネントの雛形を作成

まずは簡単のため、既存のindex.jsを改変します。

src/index.js
import React from 'react';
import ReactDOM from 'react-dom';

// HTML
const renderHTML =
<div>
  Hello, React!
</div>;

// 出力
ReactDOM.render(
  renderHTML, document.getElementById('root')
);

上記のコードを保存し実行結果のページを開くと、「Hello, React!」と表示されていることがわかります。

f:id:rennnosukesann:20180520015255p:plain

1,2行目のimportでは、react``react-domをインポートしています。react-domはHTMLを出力するのに必要です。

import React from 'react';
import ReactDOM from 'react-dom';

次に出力するHTMLをJSXの記法に従い記述しています。後ほどこのrenderHTMLを変更して出力するUIを編集していきます。

// HTML
const renderHTML =
<div>
  Hello, React!
</div>;

最後にHTMLの出力です。ReactDOM.renderメソッドは、第一引数に取るReact要素を第二引数に指定したDOMの子要素とします。

ReactDOM.render(
  renderHTML, document.getElementById('root')
);

【Shell】ターミナル上で蒸気機関車を呼ぶ

蒸気機関車とは

蒸気機関車(じょうききかんしゃ)とは、蒸気機関によって動く機関車のことである。日本では Steam Locomotive の頭文字をとって、SL(エスエル)とも呼ばれる。また、蒸気機関車、または蒸気機関車が牽引する列車のことを汽車とも言う[1]。また、明治時代には蒸気船に対して陸の上を蒸気機関で走ることから、「陸蒸気」(おかじょうき)とも呼んでいた。第二次世界大戦の頃までは「汽罐車」(きかんしゃ)という表記も用いられた(「汽罐」はボイラーの意)。 - Wikipediaより引用

Usage

$ brew install sl
$ sl
$ sl -l
$ sl -a

参考

蒸気機関車 - Wikipedia

【React】JSX

f:id:rennnosukesann:20180515230713p:plain:w300

※ここで述べるJSXはReact.jsで利用されるものを指します。

JSXとは

JSXはFacebook社が開発した、Reactの内部でHTML記述をより簡潔に書くためのJavaScript拡張構文です。

ReactではHTMLの出力を主にJavaScript上で定義します。というのも、Reactはコンポーネント指向を想定したライブラリであるためです(下記リンク参考)。

qiita.com

コンポーネント指向とは、「View(HTML)の構造・見た目と振る舞いを一つの単位とする」設計をメインに据える開発手法です。例えば、ログイン画面のログインボタンを作成する場合「ログインボタンの構造を定義するHTML」「ログインボタンのデザインを決めるCSS」「ログインボタンのロジックを定義するJS」をひとまとめにして作ります。

コンポーネント指向で設計する上で、ReactではHTML記述をJavaScript内部で行い、そこに変数や条件分岐などの動的なロジックを埋め込んでいきます。このHTML記述にJSXは用いられます。

Usage

では実際にどのようにHTMLを定義していくのでしょうか。
React標準のcreateElementを使えば、以下のようにHTMLを定義できます。

import React, { Component } from 'react';

class App extends Component {
  render() {
    return (
      React.createElement('div', null, 
        React.createElement('p', null, 'Hello, JSX!'));
    );
  }
}

しかし、これではHTMLタグを新しく追加するたびにReact.createElementを呼び出さなくてはいけません。これでは冗長すぎます。

そこでJSXの出番です。
JSXによって、上記のHTML定義を以下のように書き換えることができます。

class App extends Component {
  render() {
    return (
      <div><p>Hello, JSX!</p></div>
    );
  }
}

スクリプト中に直接HTMLタグを書いてしまいましたが、JSXが導入されていれば問題なく解釈されます。 JSXを使うことで冗長なReact.createElementがなくなっただけでなく、見た目も自然なHTMLを記述することができました!

もちろんJavaScript内のできごとなので、JSXによって記述されたHTML内に式を仕込むことだってできます。式は{}で囲むことで挿入できます。

class App extends Component {
  render() {
    return (
      <div><p>現在の時刻 : {new Date().toLocaleFormat()}</p></div>
    );
  }
}

逆に、JavaScript内部に記述しているために普段のHTMLとは異なる書き方をしなければならない箇所があります。例えばタグを識別・修飾するための属性classは、JavaScript予約語とかぶるためclassNameと記述しなければなりません。

class App extends Component {
  render() {
    return (
      <div className="cls_hello_jsx"><p>Hello, JSX!</p></div>
    );
  }
}

Install

JSXがどのようなものかはわかりましたが、どのようにReact+JSXを始めればよいのでしょうか。

実はFacebook社が提供した開発ツールであるcreate-react-appを利用することで、ビルド設定などを行わずともすぐに開発を始めることができます。

Node.jsのインストール

※検証環境は MacOS X High Sierraです。

ツールをインストールするため、Node.jsを先にインストールします。

brew install node

Windowsの方はこちらからお願いします! ダウンロード | Node.js

Node.jsをインストールすることでパッケージマネージャnpmが使えるようになるので、早速create-react-appをインストールしていきましょう。

$ npm install -g create-react-app

Windowsの方はコマンドプロンプト上で実行をお願いします。

これでインストール完了です!
create-react-appコマンドが使えるようになります。

# 適当なディレクトリを作成
$ mkdir React-JSX; cd React-JSX
# アプリを作成
$ create-react-app my-app
# 作成完了
$ ls my-app
README.md          package-lock.json  public/
node_modules/      package.json       src/

作成したアプリはすぐ実行することができます。

$ cd my-app
# 開発モードで起動(localhost:3000)
$ npm start

npm startでアプリを立ち上げるとブラウザが起動し、アプリケーションが表示されます。

f:id:rennnosukesann:20180518003117p:plain

表示できました!

このアプリケーションのView(src/App.js)はすでにJSXで記述されています。JSXをいじり始める際はここを少しずつ改良していくのもありだと思います。

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">Welcome to React</h1>
        </header>
        <p className="App-intro">
          To get started, edit <code>src/App.js</code> and save to reload.
        </p>
      </div>
    );
  }
}

export default App;

参考

reactjs.org

qiita.com

qiita.com

qiita.com

【AngularJS】AngularJSで任意のイベント通知を送信・ハンドリングする

AngularJSのイベントハンドリング

JavaScriptでは多くのイベントをハンドリングする手段がありますが、AngularJSも独自にイベントハンドリングするための機能を備えています。 例えば、AngularJSでのスクロールイベントのハンドリングは以下のように行います。

// Controller

angular.module('myApp')
.controller('MyController', ['$scope', function($scope){
    $scope.$on('scroll', function($event, args){
        // イベントハンドリング
    });
}]);

JS標準やJQueryなどと同様、イベント通知を受け取ったときに走らせるハンドラ関数を登録できます。

ところで、AngularJSではscrollなどの規定のイベントだけではなく、任意のイベント通知を送信することができます。
この仕組みを用いることで、例えば「特定のリスト項目が押されたとき」などのイベントを不特定多数のハンドラに通知することができます。

$rootScope.$broadcast

AngularJSでは$rootScope.$broadcastサービスを使うことでイベントの通知を行うことが
できます。

以下のコードは、あるコントローラCtrlAが管理するDOM領域内のボタンが押すとイベント通知が発生し、別のコントローラCtrlBがそれをハンドリングする・・・という例を表しています。

HTML
<div ng-app="myApp">
  <div class="ctrl1 mui-panel" ng-controller="Ctrl_A">
    <h3>Controller1</h3>
    value : <input id="input" type="text" ng-model="value"/>
    <br>
    <input type="button" value="broadcast" class="mui-btn mui-btn--raised mui-btn--accent" ng-click="onButtonClicked()" />
  </div>
  <div class="ctrl2 mui-panel" ng-controller="Ctrl_B">
    <h3>Controller2</h3>
    value : {{value}}
    <br>
  </div>
</div>
JS
let mod = angular.module('myApp',[]);

// 状態変数を管理し、イベント通知も行うService
mod.factory('broadcastService', [ '$rootScope', function($rootScope) {

        // 状態変数
        let sharedValue = null;

        // イベントラベル
        const VALUE_CHANGED = 'VALUE_CHANGED';

        // 状態変化+イベントを通知
        const changeValue = function broadCast(newValue) {
            sharedValue = newValue;
            $rootScope.$broadcast(VALUE_CHANGED, newValue);
        };
        
        // イベントハンドラ登録
        const assignEventHandler = function assignEventHandler($scope, handler) {
            $scope.$on(VALUE_CHANGED, function(event, newValue) {
                handler(newValue);
            });
        };

        return {
            changeValue,
            getValue,
            assignEventHandler
        };

}]);

// コントローラA
// ボタンをクリックすると、broadcastServiceの持つ状態変数にフォーム値をセットする
// 同時に状態変数が変化したことに対する通知処理が走る
mod.controller('Ctrl_A', ['$scope', 'broadcastService' , function($scope, broadcastService){
  $scope.onButtonClicked = function() {
    broadcastService.changeValue($scope.value);
  }
}]);

// コントローラB
// 「broadcastServiceの持つ状態変数が変化する」というイベントに対するハンドラを登録
mod.controller('Ctrl_B', ['$scope', 'broadcastService', function($scope, broadcastService){
  broadcastService.assignEventHandler($scope, function(newValue){
    $scope.value = newValue;
  });
}]);
実行結果

codepen.io

解説

まずHTML側を見ていきます。
HTML上ではng-controllerが修飾された箇所が2つ存在し、これらの領域は別々のコントローラによって管理されています。そのため、値valueを参照する箇所が2つありますが、参照先は両者で異なります。例えば、Ctrl_A配下のinputフォームに値を入力しても、Ctrl_B配下の{{value}}は変化しません。Ctrl_B配下の{{value}}が変化するのは、Ctrl_Bが持つ$scope.valueに値をセットしたときになります。

  <div class="ctrl1 mui-panel" ng-controller="Ctrl_A">
    <h3>Controller1</h3>
    value : <input id="input" type="text" ng-model="value"/>
    <br>
    <input type="button" value="broadcast" class="mui-btn mui-btn--raised mui-btn--accent" ng-click="onButtonClicked()" />
  </div>
  <div class="ctrl2 mui-panel" ng-controller="Ctrl_B">
    <h3>Controller2</h3>
    value : {{value}}
    <br>
  </div>

Ctrl_Aでは、HTML上のボタンに対してクリックリスナーを提供しています。
リスナーが発火すると、inputフォームに入力された値が後述するbroadcastSericeの管理する値にセットされます。同時に、値が変更した旨を知らせる通知処理が実行されます。

// コントローラA
// ボタンをクリックすると、`broadcastService`の持つ状態変数にフォーム値をセットする
// 同時に状態変数が変化したことに対する通知処理が走る
mod.controller('Ctrl_A', ['$scope', 'broadcastService' , function($scope, broadcastService){
  $scope.onButtonClicked = function() {
    broadcastService.changeValue($scope.value);
  }
}]);

Ctrl_BではbroadcastServiceの持つ状態変数が変化したことを監視するイベントハンドラを設定しています。 状態変数が変化したとき、すなわちCtrl_Aのクリックリスナーが走ったときに、$scope.valueに値がセットされてブラウザ上に表示されます。

// コントローラB
// 「broadcastServiceの持つ状態変数が変化する」というイベントに対するハンドラを登録
mod.controller('Ctrl_B', ['$scope', 'broadcastService', function($scope, broadcastService){
  broadcastService.assignEventHandler($scope, function(newValue){
    $scope.value = newValue;
  });
}]);

最後にbroadcastServiceです。 broadcastServicesharedValueと呼ばれる状態変数を持っており、この値はbroadcastService内で管理されます。

changeValueメソッドはこの状態変数を更新します。同時に、「状態変数を更新した」というイベントの通知を、$rootScope.$broadcastを使って文字通りブロードキャストします。

assignEventHandlerchangeValueが発行した通知をキャッチし、任意の処理を実行するイベントハンドラを設定します。イベントハンドラ$scope.$onで設定できます。また、実行する処理は予めassignEventHandlerの呼び出し側から設定します。

        // 状態変数
        let sharedValue = null;

        // ...

        // 状態変化+イベントを通知
        const changeValue = function broadCast(newValue) {
            sharedValue = newValue;
            $rootScope.$broadcast(VALUE_CHANGED, newValue);
        };
        
        // イベントハンドラ登録
        const assignEventHandler = function assignEventHandler($scope, handler) {
            $scope.$on(VALUE_CHANGED, function(event, newValue) {
                handler(newValue);
            });
        };

このイベント通知の仕組みによって、任意のイベントを発行できることが確認できました。
また、$scope.$onによるイベントハンドリングでは特定のUIコンポーネントやモジュールを監視し続けるということをしなくても良いので、より疎結合なコード設計にしやすくできると考えられます。

参考

AngularJS

www.muicss.com

【React】ReactとVirtual DOM

React

f:id:rennnosukesann:20180515230713p:plain:w300

ReactはFacebook社が開発するWebUIに特化したJavascriptのライブラリです。WebアプリケーションのアーキテクチャMVCで構成した場合、ReactがサポートするのはViewの部分になります。Reactを用いることで効率的にUIコンポーネントを描画したり、UIコンポーネントを管理する負担を軽減することができます。

Virtual DOM

Reactでは、Virtual DOMと呼ばれる仕組みよって効率的なUIコンポーネントの描画を行います。通常、JavaScriptではDOMによってHTML要素にアクセスし、変更を加えます。

f:id:rennnosukesann:20180516002415p:plain:w300

DOMが操作されると、ブラウザ上でレンダリング(の一部の処理)が実行されます。
ブラウザのレンダリングは以下のフローで行われるのですが、DOM操作によって「Layout」と「Paint」工程の処理が走ります。

f:id:rennnosukesann:20180516002458p:plain:w300

DOM操作を行うたびに処理が走るので、頻繁にDOM操作が実行されるとブラウザの描画パフォーマンスは悪くなってしまいます。

そこでVirtual DOMを利用します。Virtual DOMは実際のDOMとは別物であるものの、同じ構造を持ったオブジェクトです。ReactによるDOM操作が発生すると、DOMの代わりにVirtual DOMが操作されます。

f:id:rennnosukesann:20180516003214p:plain

その後、操作されたVirtual DOMの変更差分を算出して、変更部分に対応する実際のDOMを変化させます。*1これにより、DOM操作回数を通常よりも抑えることができ、上記レンダリング処理を何度も走らせない仕組みになっています。


ちなみに、Virtual DOM自体はReact固有の技術ではなく、Viewレンダリングを最適化する目的で他のライブラリ等でも実装されているみたいです。

github.com

参考

reactjs.org

Document Object Model - Wikipedia

www.shoeisha.co.jp

*1:どのタイミングでVirtual DOMの差分をDOMに反映させるかについてはまだ未調査です...