投稿者「mori-dev」のアーカイブ

React Native で Firebase Realtime Database を試した

mori-dev|2017年09月30日
JavaScript

です。今、「Firebase もくもく会(初心者向け)」に参加しています。React や redux のステートを Firebase Realtime Database に保存したら便利なんじゃないかなーと思っていたので、試しました。なおドキュメントはそんなに読み込んでいません。

試した内容

  • 「追加」を押下で +1 した結果を表示する
  • データは Firebase Realtime Database に保存する
  • リロード時には Firebase Realtime Database に保存している値を表示する
  • オフライン対応している

成果

▼ 雰囲気

気づいたコツ

へー、なるほど。使ってゆきたい、というかんじ。

React.js/redux アプリでの Google Analytics のイベントトラッキングの設定

mori-dev|2017年09月28日
DevOps

です。

React.js/redux アプリでの Google Analytics のイベントトラッキング用の設定を紹介します。

ライブラリ

以下のライブラリを用います。

他に開発用に Google Analytics Debugger を導入するとよいかもしれません。

実装

パターン1: 直接タグの属性として設定する

eventTracker のオプション attributePrefix を index.html で以下のように指定しています。こうすることで、接頭辞を ga- から data- に変更しています。ga- だと React.js が Warning: Unknown prop といったワーニングを出すからです。

ga(‘require’, ‘eventTracker’, {
attributePrefix: ‘data-‘,
});

もっともよくある設定の例です。

<NFloatingButton
className={className}
onClick={redirectToOrderPage}
data-on=”click”
data-event-category=”Request”
data-event-action=”Request”
data-event-label=””
>

onTouchTap をつけたタグの場合は、data-on=”click” ではなく data-on=”touchstart” と指定することで、動作します。

<NPrimaryButtonHalfWidth
styleName=”button”
onTouchTap={HogeAction}
label=”ほげほげ”
data-on=”touchstart”
data-event-category=”C1″
data-event-action=”A1″
data-event-label=”L1″
/>

touchstart に対応させるため、eventTracker のオプションを追加しています。

ga(‘require’, ‘eventTracker’, {
attributePrefix: ‘data-‘,
events: [‘click’, ‘touchstart’],
});

パターン2: アクションに対して設定する

一つの UI、たとえばボタンが、複数の用途で使われることがあります。その場合、その UI のタグに、直接 GA の属性を指定するわけにはゆきません。redux-beacon を使うことで、React.js のアクションに、GA の指定を対応づけることができます。

GA_FOO というアクションに対し、GA のカテゴリ C1、GA のアクション A1、GA のラベル L1 を対応づけてみましょう。

まずはアクションを定義します。

import * as ActionTypes from ‘./action_types’;
import { createAction } from ‘redux-actions’;

export const gaFoo = createAction(ActionTypes.GA_FOO);

ミドルウェアを書きます。

あとは、他のミドルウェアと同様に、この googleAnalytics ミドルウェアを applyMiddleware() して createStore() します。

結果の閲覧

Google アナリティクス の Web サイトの以下のページから、結果を閲覧できます。

  • リアルタイムデータ: リアルタイム > イベント
  • 過去データ: 行動 > イベント

Expo でフォントのロードが終わってから render するには

mori-dev|2017年09月25日
JavaScript

です。今は Expo と ReactNative と NativeBase を使っています。先程体験した問題とその対処を紹介します。

現象

console.error: “fontFamily ‘Ionicons’ is not a system font a has not been loaded through Expo.Font.loadAsync というエラーで、赤い画面になる。

対策

参考URL

 

Echo のエラーレスポンスのカスタマイズ

mori-dev|2017年08月04日
Go

です。
Echo のエラーレスポンスを、過去のブログ記事で紹介した JSON の構造にするために、以下のようなコードを書きました。

次のように設定します。

echo.HTTPErrorHandler = app_error.CustomHTTPErrorHandler

資料

goimports が go-playground/validator.v9 の import 文を自動で削除する問題の対処

mori-dev|2017年07月31日
Go

です。

問題

goimportsgo-playground/validator.v9 の import 文をファイルセーブのたびに削除してきてつらかった。

対処

import (
validator “gopkg.in/go-playground/validator.v9”
)

もっとましな方法があったら教えて下さい。

mocha のテスト実行前にグローバル変数をセットする方法

mori-dev|2017年07月24日
JavaScript

です。

mocha のテスト実行前にグローバル変数をセットする方法を調べました。

コマンド引数か mocha.opts で、設定ファイルを require することで、うまくゆきました。以下の例では、–require ./test/setup.js の他に、–require babel-register も指定しています。

 

▼ ./test/setup.js

import { JSDOM } from ‘jsdom’;

const doc = new JSDOM(‘<!doctype html><html><body></body></html>’, {
url: ‘http://localhost’,
});
global.document = doc.window.document;
global.window = doc.window;

global.window.localStorage = localStorage;
global.window.google = ‘test’;
global.navigator = {
userAgent: ‘all’,
};
global.Raven = false;
global.APP = {
‘HOGE’: ‘fuga’,
}

ぼくの考えた最強の WebAPI のエラーレスポンス型(仮)の mson を発表します!!1

mori-dev|2017年07月12日
DevOps

です。
昨日、API仕様を考えていて、社内チャットで雑談した成果になります。ご査収下さい。

 

## Error

+ status_code: `500` (number, required) – ステータスコード
+ type: `validation_error` (string, required) – エラー種別。プログラムからの利用を想定
+ logged_at `2014-10-10T13:50:40+09:00` (string, required) – エラー発生時刻。 `yyyy-MM-ddTHH:mm:ss` 形式。調査用
+ message: `validation_error_foo_bar` (string, optional) – エラーの詳細な説明
+ user_message_title: `入力内容が不正です` (string, optional) – ユーザに直接表示できるエラーのタイトル
+ user_messages: `XXXを入力してください` (array, optional) – ユーザに直接表示できるエラーの説明文の配列
+ tip: `ああするとよいかもしれません` (string, optional) – どう対処したらいいかの説明

  • logged_at はサーバーのログ出力形式と合わせる
  • 人間用のメッセージなのか、コード用のメッセージなのか明示した
  • レスポンスボディでステータスコードなどいらん、と思ってたけど改心した
  • user_messages を配列とすることで、たとえばバリデーションエラーなんかを一括してレスポンスできる

参考ページ

redux-persist でブラウザストレージに一部のデータを保存しつつ、ストアに復旧するまで render を防ぐには

mori-dev|2017年06月06日
JavaScript

です。

課題

redux-persist を使っていて、ブラウザリロード時にブラウザストレージからストアにデータを復旧する前に render されるのを防ぎたい。ストアに値があることが前提のコンポーネントでエラーになるから。公式ドキュメントとしてレシピがあった(Delay Render Until Rehydration Complete)  が、これだと、すべてのステートがローカルストレージに保存されてしまいます。

前提

  • redux-persist で、すべてではないいくつかのステートをローカルストレージとセッションストレージに保存しています
  • ステートは { loadingFlag: true, data: { foo: ‘bar’ }} といった構造で、data 以下だけをブラウザストレージに保存します
  • UniversalJS を意識した設計になっており、プロバイダを提供するコンポーネントが、ネイティブアプリ(Cordova)版 と ブラウザ版で別にあります

解決案

公式ドキュメントのレシピは、persistStore するときのオプション指定が行われていないことが、今回の私たちの状況にとって問題です。rehydrated か否かを redux のストア common.rehydratedFlag として、プロバイダを render するコンポーネントをコンテナにして、それを参照すれば済むのではないかというのが、最初の案でした。しかし、connect するときに store がないといったエラーになり、うまくゆきませんでした。

redux-persist の persistStore では第三引数で、処理終了時のコールバックを指定できます。そこで、ここで rehydrate の終了イベントを emit して、プロバイダコンポーネントでリスンして、プロバイダコンポーネントのステートとして用意した rehydratedFlag を true にする、という対応をとりました。これでプロバイダコンポーネントが Cordova アプリ、ブラウザアプリと複数ある場合も、それなりに DRY なまま、望んだ結果を表現できました。

実装の概略は以下の通りです。

環境

React コンポーネントで自分の子要素を取得するときのイディオム

mori-dev|2017年05月25日
JavaScript

です。

課題

React コンポーネントで自分の子要素を取得するときに、ref=”payjpCheckoutRef” などと指定しておいて、findDOMNode(this.refs.payjpCheckoutRef) していたら、eslint で警告がでていた(のをつまらないしめんどくさいからコメントをつけてやりすごしていた)。ref=”payjpCheckoutRef” に対しては、Using string literals in ref attributes is deprecated、 findDOMNode(this.refs.payjpCheckoutRef) に対しては、Using string literals in ref attributes is deprecated

対策

ビフォー

 

componentDidMount() {
this.payjpCheckoutRef = findDOMNode(this.refs.payjpCheckoutRef);
this.payjpCheckoutRef && this.payjpCheckoutRef.appendChild(this.script);
}

render() {
return <div ref=”payjpCheckoutRef”></div>;
}

アフター

 

componentDidMount() {
// this.payjpCheckoutRef に ref で指定した要素が格納されている
this.payjpCheckoutRef && this.payjpCheckoutRef.appendChild(this.script);
}

render() {
return <div ref={node => this.payjpCheckoutRef = node}></div>;
}

元ネタ

gaearon さんの findDOMNode 批判が有益でした。

宣伝

ランサーズの新規事業 pook では、Wantedly に正社員希望の Rails エンジニアの求人を出しています。

redux アプリで定期実行処理を書くには

mori-dev|2017年05月02日
JavaScript

です。redux アプリで redux-saga を使って定期実行処理を書いてみました。

要件

redux-saga で以下を実装する方法を検討しました。

  • 定期的にステート(本稿では reducer で分割したストア)の値をチェックして何かする
  • 定期的に API コールを行い、ストアを更新する
  • アプリが起動している間ずっとではなく、開始条件と終了条件をつけたい。
  • 終了条件は複数ある

答案

定期的にストアの値をチェックして何かするの実装例を書いてゆきます。redux-saga の公式ドキュメントの cancel の説明が参考になります。これをみつけられるかがキモかと思います。

ActionTypes

 

_TRIGGER、_STOP、_EXEC、_END の4種類を一組として、CHECK_ ではじめるルールとしました。

  • ActionTypes.CHECK_SAMPLE_TRIGGER
  • ActionTypes.CHECK_SAMPLE_STOP
  • ActionTypes.CHECK_SAMPLE_EXEC
  • ActionTypes.CHECK_SAMPLE_END

以下の2つは redux-saga で実行するアクションのアクションのタイプです。

  • ActionTypes.CHECK_SAMPLE_EXEC //=> これがビジネスロジックの開始合図
  • ActionTypes.CHECK_SAMPLE_END

以下の2つは redux-saga 以外で実行するアクションのタイプです。

  • ActionTypes.CHECK_SAMPLE_TRIGGER //=> saga への定期実行開始依頼
  • ActionTypes.CHECK_SAMPLE_STOP //=> saga への定期実行終了依頼

アクション

 

アクションの例です。

特徴はありません。

リデューサー

 

リデューサーは必要ないので作りません。

saga

 

単純な redux-saga の例です。ActionTypes.CHECK_SAMPLE_STOP と ActionTypes.LOGOUT_OK の2つのアクションを終了条件としました。

ミドルウェア

 

私たちのプロジェクトでは、ビジネスロジックはミドルウェアに書いています。以下は単純なミドルウェアの例です。
▼ src/middleware/periodic_inspections/sample.js

ミドルウェアでは次のようにステートを取り出せます。

const stateName = store.getState().stateName;

テスト

 

redux-saga の基本的なテストの例です。

「定期的に API コールを行いストアを更新する」処理も似たようなものです。

 

Webpack で git 管理してもよいような環境ごとに値の異なる定数を設定する方法

mori-dev|2017年04月13日
JavaScript

です。仕事でのちょっとしたコミットをブログ記事化するシリーズです。今日は Webpack の話題です。

課題

環境ごとに値の異なる定数として、git で管理できるものと git で管理すべきではないものがあります。後者は dotenvWebpack の DefinePlugin で実現できます。git で管理してよいような、環境ごとに値の異なる定数はどう扱えばよいのでしょうか。

答案

webpack-merge で環境ごとに設定ファイルを分離して、Webpack の DefinePlugin と組み合わせてみました。webpack-merge の smartStrategy で plugins 欄の扱いをコントロールします。

production 環境でだけ違う値を使いたい場合は、webpack.config.base.js の値を webpack.config.production.js の値で上書きできるように設定した上で、webpack.config.base.js でデフォルト値を指定して、webpack.config.browser.production.js で production 用の値を指定します。以下は webpack.config.browser.production.js の例です。

const config = merge.smartStrategy({
plugins: ‘prepend’ // DefinePlugin の base の値を上書きできるようにする
})(baseConfig, {優先される設定})

module.exports = config;

補足

flowtype 対策が必要かもしれません。たとえば APP.HOGE という定数だとすると、decls/my_app_types.js に `declare var APP: Object;` と記述します。
eslint 対策が必要かもしれません。たとえば APP.HOGE という定数だとすると、.eslintrc.yaml に以下のように記述します。ワードプレスの引用記法では、インデントが崩れている点はご注意下さい。

globals:
APP: true

テストが落ちないようにする必要もあります。たとえば APP.HOGE という定数だとすると、 test/util/global.js に以下のように書いた上で、

global.APP = {
‘HOGE’: ‘3000’,
}
export default global;

次のように呼び出しておきます。

/* eslint-disable no-unused-vars */
import global from ‘../util/global’;

環境

  • webpack v2.3.3
  • webpack-merge v4.1.0

コールバックスタイルの外部ライブラリなどの関数を redux-saga で使うには

mori-dev|2017年04月11日
JavaScript

です。本日の課題とその対処です。

課題

外部ライブラリの関数が、コールバックスタイルのため、redux-saga の自前実装部分との実装の統一感がない。たとえば、navigator.geolocation.getCurrentPosition で現在地を取得することを考えます。以下のようなコードをコンポーネントに書くのは微妙です。

対処

Promise でラップすることで、redux-saga の call が使えて、普通の API コール系と同様に書けるようになります。他とアプローチを揃えておくと、将来的な抽象化の可能性などもあがっていいですね。

コンソールで git のコミットを peco で検索して emacs に表示する

mori-dev|2017年04月06日
ツール/ライブラリ

です。わりと最近書いた便利なシェルスクリプトのご紹介です。

要件

私たちのプロジェクトは、Git のコミットがある程度きれいです。個々のコミットやプルリクがクックブックのように利用できます。コミットメッセージをキーにコミットを検索して、その内容を表示したいです

対処

peco で絞り込んで emacs に表示するシェルスクリプトを作成しました。

▼ ターミナルで「ローカル」でコミットメッセージを絞り込んだ様子

▼ emacs にコミットの内容が表示された様子

react-redux v5.0.0でredux-form, material-uiを併用時、フォームの日本語入力が1文字目で確定される問題の対策

mori-dev|2017年04月04日
JavaScript

です。しばらく日々のこまごまとした課題とその解決をブログに書き残しておくことにしました。

現象

redux-form の入力欄(Field)を material-ui(TextField)で作成しています。react-redux の v5.0.0-rc.1 から v5.0.0 へのアップグレード時に、フォームの入力欄で「ぬ」と入力したいのに「n」で入力確定されてしまうようになりました。

環境

  • react-redux v5.0.0
  • material-ui v0.17.0
  • redux-form v6.5.0

対策

回避する対策としました。

react.js/redux/webpack/flowtype なプロジェクトでの stylelint の導入手順

mori-dev|2017年02月10日
JavaScript

こんにちは。です。

最近コードレビューで「CSS の `:` のあとにスペースがあったりなかったりします」と指摘され、つい悲しくなって stylelint を導入しました。react.js/redux/webpack/flowtype なプロジェクトです。こちらのサンプルアプリへのプルリクが手順です。

もう同じような指摘を受けないように、stylelint が頑張ります!