あいつの日誌β

働きながら旅しています。

Facebook の fiber が新しい UI components かと早とちりした件

あらすじ

以下のような記事を見つけたので facebook がいい感じの UI components を出したかと思って興味をもちました。

jp.techcrunch.com

で以下の動画を見た。どうやら期待していた話とはちがっている模様。UI component な話ではないらしい。

www.youtube.com

そもそも React.js が今までやってくれている事と fiber で変わる事

いわゆる仮想DOMと呼ばれているのは概ね下記の事なのかな?

  • reconciliation: DOM の差分を出すために色々計算する
  • update: setState でデータを更新し、最終的に再レンダリングをする

で、fiber は reconciliation の部分を新しいアルゴリズムに変えたらしいです。なので互換性自体は損なわれないはず。

というわけで私が最も気になっていたのは「JavaScript の UIフレームワークを完全リニューアル」がいい感じの UI components が出たの??という事だったのですがどうやらちがった。

結論

  • UI components ではないらしい
  • Fiber 自体がアルゴリズムを再実装した React.js のことらしい
  • 細かい事はさておき、恩恵を受けるには素直に最新 version に追従していればいい

というわけで fiber 自体は非常に興味深いのですが、私がとる選択は「素直に最新バージョンが出たら追従する」で「他に興味がある事柄に注力する」です。

とはいえ

最新のバージョンに追従すると既存のコードに影響が出てしまったり、他のタスクも平行して進めなければならない場合がほとんど。

そんななかフロントエンドを一騎駆けしないといけない状況(援軍が来ない)だとバージョンアップの追従をだんだんやらなくなっていくんだよなあ...

良識のある偉い人のみなさまにはぜひフロントエンドに友軍を送り込んで頂きたい。

Node.js v7.6.0 以降で async と await で逐次処理を書く

Node.js v7.6.0 から async と await が --harmony フラグ無しで使えるようになっています。

以下のような逐次処理を書くときに簡単に書けるようになりました。

async function x() {
  let total = 0 
  for (let i = 0; i < 100; i++) {
    total += await Promise.resolve(i)
  }
  return total
}

x().then(console.log) // 4950

逐次処理ではなくて並列処理をしたい場合は Promise.all を使うとよいでしょう。

function sleep(ms = 0) {
  return new Promise(r => setTimeout(r, ms))
}

// async をつけて Promise を return する
async function y() {
  for (let i = 0; i < 100; i++) {
    await sleep(1)
  }
}

// Promise を return しているので async はつけない
function z() {
  const tasks = []
  for (let i = 0; i < 100; i++) {
    tasks.push(sleep(1))
  }
  return Promise.all(tasks)
}

console.time("y")
y().then(() => console.timeEnd("y")) // 151.229ms

console.time("z")
z().then(() => console.timeEnd("z")) // 10.434ms

react-script build すると process.env.NODE_ENV === "production" になる

title で全ての説明が終わった気がしますが以下のようなコードを書いていて

const apiUrl = (process.env.NODE_ENV !== "build") ? "http://localhost:3041/api" : "/api"

package.json には以下のような記述をしていましたが process.env.NODE_ENV が build にならないので何でだろうと思いました。

"build": "NODE_ENV=build react-scripts build"

console.log を仕込んだところ結果は下記の通り

Object {NODE_ENV: "production", PUBLIC_URL: ""}

最初に出てきていたコードは下記の通り修正して無事動作しました。おしまい。

const apiUrl = (process.env.NODE_ENV === "production") ? "http://localhost:3041/api" : "/api"

hachioji.pm #65 に行ってきた

66回目のはちぴーに行ってきました。

https://atnd.org/events/86804atnd.org

開催された場所: や台ずし 八王子 横山町

f:id:okamuuu:20170325190021j:plain

割と安くて美味しいいいお店でした。開店~19時迄ドリンク半額なのでちょっとお得。

nintendo switch

f:id:okamuuu:20170325180959j:plain

みんなでガンマンになったり真剣白刃どりしたり。この会の参加者は新しいモノが好きな人が多いですね。

LT

hkoba: mosh

途切れがちなネットワークで効果があったそうです。

macopy: kuiperbelt と入院

Commiter が特定の人になってきているそうです。 あと鎖骨のお話

myfinder: GVFS

ほかにも MS でのエンジニアの取組みについてなど。

私: フロントエンドのはじめかた

一人でやらずにみんなでやりましょう。https://okamuuu.github.io/talks2

papix: IR kit

エアコンを付けたり消したり

neko_gata_s(shinpei): NekogataDrumSequencer

MVVMの文脈から見る、ViewModelとModelの関係は執筆中 https://shinpeim.github.io/NekogataDrumSequencer/build/

uzulla: PHP7.1 ベンチマーク

wrk doesn't work は英語圏の人にうけたそうです

makamaka_at_donzoko: 同人誌

電子化発表!

フロントエンドのはじめかた

一人でやると色々大変なのでチームメイトと分業して作業しましょうという話をしました。React.js の場合はコーダーさんに components を作ってもらって storybook への commit をお願いするところまでレクチャーするとかなり捗ると思います。

あとそういうハンズオンをやりたい気分です。次のステップに進みたがっているHTMLコーダーさんが集まる勉強会どこかにないかな?

Google の QUIC プロトコルは勉強したほうがいいですか?

参加する直前に知り合いのネットワークエンジニアがインフラを QUIC に対応させる云々言っていたのでそれは私(サーバーサイドエンジニア)も勉強する必要あるのか聞いてみました。

Nginx の config いじるとかそういうレイヤーは関係ないそうです。職務的に関係するとしたら HTTP2 のほうではないでしょうか。との意見を頂きました。

ガンマン

こちらからは以上です。

React.js の tutorial (2017 spring)を書きました

github.com

React.js で SPA しようとすると redux が登場するので学習コストが割と高くなると思ったのでいっその事 redux 使わなければいいんじゃないかと思いました。README.md に手順を書いてあるのでお試しください。

一応日本語で書いたものをなんとなく Qiita に書きました。 http://qiita.com/okamuuu/items/1b94ab69d1bc3fb82d27

react-router@v4 を調査した

あらすじ

react-router@v4 にしたら色々な変更が入っていて全く動かなくなったので react-router の機能を確認しました。

追記: 2017-03-23 16:30

withRouter について追記しました。

言及すること

  • <Route> の挙動が v3 と異なる
  • <Switch><Route exact ...> の挙動を理解して対処しましょう
  • browserHistory.push から `this.props.history.push へ変わった
  • <NavLink> が追加されました
  • Nested Routes が廃止された?
  • withRouter

準備

create-react-app practice-react-router4 && cd $_
npm install --save react-router-dom

<Route>

最初に src/App.js を以下のように修正します。

import React from 'react'
import {
  BrowserRouter as Router, Switch, Route, Redirect,  Link, NavLink, withRouter
} from 'react-router-dom'

const Home = () => (<h2>Home</h2>)
const About = () => (<h2>About</h2>)
const User = () => (<h2>User</h2>)

const App = () => (
  <Router>
    <div style={{padding: "60px"}}>
      <Route path="/" component={Home} />
      <Route path="/about" component={About}/>
      <Route path="/:user" component={User}/>
      <Route component={NoMatch}/>
    </div>
  </Router>
)

export default App 

この状態で http://localhost:3000/, http://localhost:3000/okamuuu, `http://localhost:3000/about にアクセスするとそれぞれ以下の画面が表示されます。

open http://localhost:3000/

<div data-reactroot="" style="padding: 60px;">
  <h2>Home</h2>
  <h2>NoMatch</h2>
</div>

open http://localhost:3000/okamuuu

<div data-reactroot="" style="padding: 60px;">
  <h2>Home</h2>
  <h2>User: okamuuu</h2>
  <h2>NoMatch</h2>
</div>

open http://localhost:3000/about

<div data-reactroot="" style="padding: 60px;">
  <h2>Home</h2>
  <h2>About</h2>
  <h2>User: okamuuu</h2>
  <h2>NoMatch</h2>
</div>

このような <Route> の使い方をすると、version4 では URL にマッチする component を全て含んでしまいます。

<Swtich>

先ほどの <Route>s の直上に <Switch> を加えます。

 const App = () => (
   <Router>
     <div style={{padding: "60px"}}>
+      <Switch>
         <Route path="/" component={Home} />
         <Route path="/about" component={About}/>
         <Route path="/:user" component={User}/>
         <Route component={NoMatch}/>
+      </Switch>
     </div>
   </Router>
 )

この状態で http://localhost:3000/, http://localhost:3000/okamuuu, `http://localhost:3000/about にアクセスすると全て以下の画面が表示されます。

<div data-reactroot="" style="padding: 60px;">
  <h2>Home</h2>
</div>

これは最初に Match する要素が発見され次第、その要素だけを返すからです。いまのままでは全ての location に対して <Home> を返してしまうので以下のように正確に Match した場合だけ判定するように <Route>exact=true を追加します。

open http://localhost:3000/

<div data-reactroot="" style="padding: 60px;">
  <h2>Home</h2>
</div>

open http://localhost:3000/okamuuu

<div data-reactroot="" style="padding: 60px;">
  <h2>User: okamuuu</h2>
</div>

open http://localhost:3000/about

<div data-reactroot="" style="padding: 60px;">
  <h2>About</h2>
</div>

browserHistory.push

以下のように props で受け取る形式に変わっています。

-const User = (props) => (<h2>User: {props.match.params.user}</h2>)
+const User = (props) => (
+  <div>
+    <h2>User: {props.match.params.user}</h2>
+    <button onClick={() => props.history.push("/")}>Back to Home</button>
+  </div>
+)

<NavLink>

src/App.js を以下のように修正します。

 const App = () => (
   <Router>
     <div style={{padding: "60px"}}>
+      <ul>
+        <li><NavLink to="/">Home</NavLink></li>
+        <li><NavLink to="/about">About</NavLink></li>
+        <li><NavLink to="/okamuuu">User</NavLink></li>
+      </ul>
       <Switch>
         <Route exact path="/" component={Home} />

Match する場合は className に active が追加されます。

open http://localhost:3000/

<ul>
    <li><a class="active " href="/">Home</a></li>
    <li><a href="/about">About</a></li>
    <li><a href="/okamuuu">User</a></li>
</ul>

open http://localhost:3000/okamuuu

<ul>
    <li><a class="active " href="/">Home</a></li>
    <li><a href="/about">About</a></li>
    <li><a class="active " href="/okamuuu">User</a></li>
</ul>

open http://localhost:3000/about

<ul>
    <li><a class="active " href="/">Home</a></li>
    <li><a class="active  href="/about">About</a></li>
    <li><a href="/okamuuu">User</a></li>
</ul>

便利な機能ですがこのままだと / も Active になってしまいます。<Route> の時のように extract を指定します。

-        <li><NavLink to="/">Home</NavLink></li>
+        <li><NavLink exact to="/">Home</NavLink></li>

また CSS in JS したい場合は className を各自で好きなように指定したい場合は以下のようにします。

CSS in JS したい

<NavLink
  to="/about"
  activeStyle={{
    fontWeight: 'bold',
    color: 'red'
   }}
>About</NavLink>

className を好きにしたい

<NavLink
  to="/about"
  activeClassName="selected"
>About</NavLink>

個人的な意見ですが NavLink を使うような components はロジックと分離したいのであまり使わないかもしれないのですが一応使い方は覚えておいたほうがいいと思います。

ロジックの分離: About がクリックされたら親コンポーネントに「今 About が押されたよ。あとよろ」と実装して web components として再利用しやすい状態にしたい。

Nested Routes 廃止?

v3 では共通の components を使用したい場合は {this.props.children} と書いて Nested Routes していたのですが、v4 からはそうではなくなっているようです。

withRouter

Home へ戻るボタンを追加したいのですが、ボタン自体に hisotry オブジェクトを渡さない場合、呼び出し元で hisotry.push 関数を実行したいのですが react-router@v4 から browserHistory が import できなくなっています。

代わりに withRouter を使います。

edit src/App.js

const HomeButton = ({onClick}) => (
  <button onClick={onClick}>push Home</button>
)

const Routes = withRouter((props) => {
  const {push} = props.history

  return (
    <div style={{padding: "60px"}}>
      <ul>
        <li><NavLink exact to="/">Home</NavLink></li>
        <li><NavLink to="/about">About</NavLink></li>
        <li><NavLink to="/okamuuu">User</NavLink></li>
      </ul>
      <Switch>
        <Route exact path="/" component={Home} />
        <Route exact path="/about" component={About}/>
        <Route path="/:user" component={User}/>
        <Route component={NoMatch}/>
      </Switch>
      <HomeButton onClick={() => push("/")} />
    </div>
  )
})

const App = () => (
  <Router>
    <Routes />
  </Router>
)

まとめ

というわけで v3 から v4 に移行する場合、小さなアプリはいいと思いますがそうでない場合はちょっと面倒くさいかもしれません。こちらからは以上です。