React context APIを触ってみた

元ネタ

medium.com

React v16.3でcontext APIというものが入った

このAPIを使うとReduxと同じようなデータフローを簡単につくれるらしい

記事中にわかりやすい説明があるので動かしてみた

環境構築

create-react-app をベースに使う

$ npx create-react-app my-app

ちなみに npx は、グローバルインストールして使うようなnpmコマンドを落とさず、その場限りで実行させられるお手軽なツール

普通に create-react-app my-app やっても良い

www.npmjs.com

この記事を書いてる現時点だと、Reactのバージョンがv16.2なので手動でアップグレードする

$ yarn add react@16.3.0-alpha.1 react-dom@16.3.0-alpha.1

動かしてみる

わかりやすくApp.jsにまとめて書く

<Provider>で渡したvalueが、その内部で書いた<Consumer>に渡ってくる感じ

import React, { createContext } from 'react'

// context api
const Context = createContext()
const { Provider, Consumer } = Context

class App extends React.Component {
  state = {
    count: 0
  }

  render() {
    return (
      <Provider
        value={{ // 共有したい値を渡す
          state: this.state,
          actions: {
            increment: () => this.setState({ count: this.state.count + 1 }),
            decrement: () => this.setState({ count: this.state.count - 1 })
          }
        }}
      >
        <Counter />
      </Provider>
    )
  }
}

const Counter = () => (
  <Consumer>
    {({ state, actions }) => ( // Providerで渡したvalueがそのまま返ってくる
      <div>
        <span>{state.count}</span>
        <button onClick={actions.increment}>+1</button>
        <button onClick={actions.decrement}>-1</button>
      </div>
    )}
  </Consumer>
)

export default App

// index.js
// ReactDOM.render(<App />, document.getElementById('root'));

動いた

f:id:rskull:20180302014642g:plain

https://rskull-sandbox.github.io/react-context-api/

github.com

react-stateful

context APIをラップして、よりReduxっぽい仕組みを取り入れたのがreact-stateful

github.com

ソースみたら1ファイルで100行も無かった

すごいシンプルなものなら楽に作れそう

GoogleHomeとIoTデバイスでオフィスのQOLを上げた話

GoogleHomeとIoTデバイスを使ってオフィス環境の向上と

仕事が効率化するように色々やったので紹介しようと思います

前提として、うちの会社(Togetter社)はオフィスに10名ちょっとしかいないので、GoogleHomeに好き勝手喋らせてもわりと大丈夫です

プログラム郡はすべてラズパイ上で動かしています

永続化はforever

API経由でGoogleHomeに好きな言葉を喋らせる

記事を探せばたくさん出てくるこのライブラリを使う

github.com

const googlehome = require('google-home-notifier')

googlehome.device('Google-Home', 'ja')
googlehome.notify('こんにちは', () => {})

これだけで喋ります

汎用的にするために、用意したAPIにワードをPOSTすると喋ってくれるようにしてみます

基本的な手順は前に書いた記事とほぼ同じなので、ざっと説明します。詳しくは下のリンクを見て下さい。

1. Fireabseの設定

Firebaseのデータベースを設定します

f:id:rskull:20180226223025p:plain

こんな感じの構造にします

2. Firebaseの更新を監視するコードを書く

const firebase = require('firebase');
const googlehome = require('google-home-notifier')

googlehome.device('Google-Home', 'ja')

// firebase
const config = {
  apiKey: "xxxxxxxxx",
  authDomain: "xxxx.firebaseapp.com",
  databaseURL: "https://xxxx.firebaseio.com",
  projectId: "xxxx",
  storageBucket: "xxxx.appspot.com",
  messagingSenderId: "01234567"
}

firebase.initializeApp(config)

const db = firebase.database();
const ref = db.ref('/googlehome');

ref.on('value', function(changedSnapshot) {
  const key = changedSnapshot.key;
  const text = changedSnapshot.child('text').val();

  if (text !== '') {
    // GoogleHomeに喋らせる
    googlehome.notify(text, () => {})
    ref.set({ text: '' })
  }
})

これでデータベースのtextカラムが更新されると、そのテキストを喋ってくれるようになりました

3. IFTTTを設定

New Appletを押して

if Webhook then Webhook な設定をします

最初のWebhookのトリガーは1つしかないので、そのままそれを選択します

Event Nameはなんでも良いので、適当なものを設定してください

f:id:rskull:20180226224525p:plain

次にthenのWebhookでは、APIからきたリクエストをFirebaseに書き込む設定をします

f:id:rskull:20180226225104p:plain

保存して適用しましょう

下記のページにアクセスして、Documentationと書いてあるページに飛ぶと

エントリーポイントとなるURLが確認できます

Do more with Webhooks - IFTTT

$ curl -X POST -H "Content-Type: application/json" -d '{"value1": "こんにちは"}' \
       https://maker.ifttt.com/trigger/googlehome/with/key/xxxxxxxxxxxx

curlでテストすると喋ってくれるようになしました

好きなことを喋らせられる機能

これは実験的ですが、上で作ったAPIを叩くフォームを作りました

Firebaseに書き込めばなんでも喋るので直接書き込んでもいいですが、出来るだけ楽をしたかったのでこうしました

もちろん社内からしかアクセスできません

公開したらみんな変なこと喋らせるのですぐ閉じました :(

カレンダーのスケジュールをアナウンスさせる

IFTTTを使って設定しました

if GoogleCalender then Webhook

トリガーにGoogleCalenderのAny event starts を選択

このトリガーはスケジュールのn分前(任意の設定)になると発火するので、その後に

WebhookでFirebaseのgooglehomeに書き込むようにすれば、予定をアナウンスしてくれるようになります

ゴミ当番を指名してアナウンス

小さいオフィスなので、ゴミ捨ては自分たちでやります

人がいたりいなかったりして当番制もやりにくいので抽選するプログラムを書きました

弊社ではとある勤怠管理Webサービスを使っていて、そこのAPIから出勤してる人一覧を取れます

なので、単純に出勤してる人の中から1名をランダムで抽出して喋らせるバッチを書いて

それをcronでゴミの日に実行されるようにすることで

「今日のゴミ当番は○○さんです。よろしくお願いします」とアナウンスさせることが出来るようになります

機会に言わせることで無駄な争いを生むこともありません。素敵ですね!

室内環境を監視してアラートさせる

去年、弊社では室内の二酸化炭素濃度を計測するモニターを買って、濃度が高くなったら換気をするという習慣をつけるようにしました

※ 弊社社長の去年のツイート

二酸化炭素濃度はppmという単位で表されます。

室内の最適なppm値はだいたい400 ~ 800くらいです。1000ppmがギリギリで、それ以上になってくると

頭がぼーっとしてきたり、息苦しくなってきます。

このモニターを使って毎日室内のCo2濃度を監視していたのですが、濃度が高くなったらGoogleHomeに警告させたいよね!

となったので、探してみたらAwairというIoTデバイスを発見しました

日本サポートもあり、技適も通ってるので大丈夫そうです

getawair.com

Awairは温度・湿度・Co2濃度・化学物質・ホコリを警告してくれる高性能なデバイスです

化学物質ってなんだ?と思っていましたが、シンナー臭い除光液とか使ってると反応して数値が高くなります(体験談

GoogleHomeと同じで、スマホにアプリを入れて連帯することで本体の設定や数値の確認をすることが出来ます

IFTTTに対応していて、各項目の数値が高くなったりしたときにトリガーすることが出来るので

さっきと同じようにWebhookFirebaseに投げてGoogleHomeにアナウンスさせるようにしました

デモ

これにより、弊社のオフィス内は温度・湿度・Co2濃度・化学物質・ホコリともに出来るだけ最高の状態を保つようにしています!

加湿機も3台フル稼働していて、なかなか忙しない感じです

室内環境で、とくに空調は体にダイレクトに影響するので

これらのデバイスを組み合わせてどんどん機会に支配されていきましょう!

終わりです。

今更だけどparcelの素振り

parcel + react + reduxを動かしてみる

環境構築

セットアップの仕方は公式サイトに親切に書いてある

🍰 Recipes

parcelをグローバルインストール

$ yarn global add parcel-bundler

ディレクトリを作成

$ mkdir parcel & cd parcel
$ yarn init -y

まずはドキュメント通り、react + parcelだけの必要最小限のパッケージをインストールする

$ yarn add react react-dom
$ yarn add --dev parcel-bundler babel-preset-env babel-preset-react

.babelrc

{
  "presets": ["env", "react"]
}

コードを書く

index.html

<html>
  <body>
    <div id="app"></div>
    <script src="./index.js"></script>
  </body>
</html>

index.js

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

class Hello extends React.Component {
  render() {
    return <div>hello world</div>
  }
}

const app = document.getElementById('app')
ReactDOM.render(<Hello />, app)

実行

$ parcel index.html

エラーなく立ち上がれば http://localhost:1234 で繋がるようになる

f:id:rskull:20180212234043p:plain

アクセスしてブラウザにhello worldが表示されていれば成功

reactもちゃんと動いてる

ビルド後のソースは dist フォルダに生成される

dist
├── index.html
├── parcel.js
└── parcel.map

reduxを導入する

$ yarn add redux react-redux

最小限だとこれだけで動く

前書いた記事のreduxの最小限の構成を輸入したら

問題なく動いてたので成功した

https://github.com/rskull/parcel-react

感想

簡単

素振りおそすぎた

追記 (2018/02/14)

本番ビルドだと動かなかった

メソッドがないよってエラーが出る

なぜ

https://rskull-sandbox.github.io/parcel-react/

ReactNative アップグレード対応 v0.46 -> v0.52

最近、苦行のアップグレード対応をした

RN v0.46からv0.52にアップグレードした時に色々エラーで引っかかったので

なにを修正したかのメモをする

$ react-native-git-upgrade はうまく動かなかったので使ってない

自力で差分をみてマージした

前提として、iOSのプロジェクトにはCocoapodsを導入してる

テンプレートを更新

最初からあったファイル群を更新する

以下のURLでバージョンを指定すると差分が見れる

Comparing rn-0.46.4...rn-0.52.2 · ncuillery/rn-diff · GitHub

package.json

差分を見ながら、最初から入ってたモジュールのバージョンを上げた

書き換えた後 ↓

"react": "16.2.0",
"react-native": "0.52.2",
"babel-jest": "22.1.0",
"babel-preset-react-native": "4.0.0",
"flow-bin": "0.61.0",
"jest": "22.1.4",
"react-test-renderer": "16.2.0"

.flowconfig

差分を見ながら新しく追加された設定と変わった設定を手動マージ

テストが落ちてたら、ほぼ自分が設定したところが悪いので自力で直す

.gitignore

差分をみて手動マージ

その他

build.gradle, MainApplication.java にも変更があったが、ここはいじらなかった

v0.49で、初回のエンドポイントが変わった事による変更なので

v0.49より前から開発してた場合は直さなくても問題は無い

気になるなら直しても良い

残りは必要ないので無視

サードパーティー(npm)の更新

導入した react-native-xxxx 系のモジュールを更新する

RNをアップグレードする際、リリースノートを見るとBreaking changesが幾つかあるので

たいていそれ関連のエラーが出る

サードパーティーのモジュールが対応してくれていれば、大体アップグレードするだけで直る

その過程でハマったエラーを紹介

propTypes関連のエラー

iOS/Android

2018-01-24 19:12:12.104554+0900 togetter_app[43599:6074808] undefined is not an object (evaluating 'b.propTypes.style')

このエラーはコード中にView.propTypes が書かれていると起こる

import { ViewPropTypes } from 'react-native'
// View.propTypes -> ViewPropTypes

書き方が変わったので修正するだけ

依存してるサードパーティーも影響するので、対応してくれてないと困る

未だに対応されてなかったら捨てたほうが良さそうだけど、それが無理ならフォークして直すしかない

App is crashing on launch - only on Release build · Issue #17276 · facebook/react-native · GitHub

react-native-device-info

iOSでの警告

2018-01-26 13:39:28.881 [warn][tid:NSOperationQueue 0x600000425c40 (QOS: UNSPECIFIED)][RCTModuleData.mm:250] RCTBridge required dispatch_sync to load RCTDevLoadingView. This may lead to deadlocks

podを使ってインストールしてると出る警告

回避するにはpodに書くのをやめて、通常通りreact-native link ... の方法にするしかなかった

react-native-device-infoにかぎらず、このエラーが起きた場合はこの方法で大体直る

根本的なエラーの原因はよく分からなかった

RCTBridge required dispatch_sync to load RCTDevLoadingView. This may lead to deadlocks · Issue #16376 · facebook/react-native · GitHub

react-native-vector-icons

error: bundling failed: Error: While resolving modulereact-native-vector-icons/MaterialIcons, the Haste packagereact-native-vector-iconswas found. However the moduleMaterialIconscould not be found within the package. Indeed, none of these files exist: ...(省略)

RN v0.52.1 だと出るエラー

回避策はあったけど、RN v0.52.2 で直ったので問題なくなった

the Haste package 'react-native-vector-icons' · Issue #630 · oblador/react-native-vector-icons · GitHub

requiresMainQueueSetup

iOSでの警告

f:id:rskull:20180201174251p:plain

デバッガーにいくつか警告が出た

おそらくRN v0.48から追加された変更で

ネイティブモジュールのブリッジ部分にrequiresMainQueueSetupを設定する必要ができた

こんな感じで ↓

Fix RN 0.48+ warning about requiresMainQueueSetup by doochik · Pull Request #242 · rebeccahughes/react-native-device-info · GitHub

依存してるサードパーティーが対応してくれていれば、アップグレードでエラーは消えた

しかし、ビルドインのはずのRCTImageLoaderでも同じ警告が出てた…

どうしようもないので修正待ち。デバッガーにしか警告はでなくて動作にも影響してないようなので

とりあえずは問題なさそう

Androidでビルドエラー

error: method does not override or implement a method from a supertype @Override ^ ...(省略)

導入してるネイティブモジュール関連でエラーが出た

これはRN v0.47で入ったAndroidのBreaking changesによるもの

これも対象のnpmのアップグレードで対応した

変更は削除だけっぽい

Remove override of createJSModules by ptomasroos · Pull Request #446 · evollu/react-native-fcm · GitHub

疲弊した

対応したのはこれくらい

色々ハマりすぎて疲れた

アップグレードをサボるほど辛くなるの厳しい