taiki-t's diary

React Native, Rails そして雑多な記録: The world is waiting for you to give it a meaning.

Relay Modernへの移行検討をした

結論: もう少しドキュメントとサンプルが増えてからにしよう(rcが外れる頃かな?)

追記: 2017/08/21: 移行しました: Relay Modernに移行した - taiki-t's diary

背景

現在アプリを作っていて、フロント側の構成はReact Native + Relay + Reduxという感じだ。

(Relay Modern以前のバージョンはModernの登場によりRelay Classicと呼ばれるようになったのでこの記事でも以後Relay Classicと呼ぶ。)

ネットワークを介するデータについてRelay Classicは素晴らしく機能的だったが、ネットワークを介さないデータの管理には向いていない。というかRelay Classic標準ではできない。

そこで出てくる問題は、コンポーネントを跨いだアプリケーショングローバルのステート管理だ。(コンポーネント単位のローカルステートであればそのままReactのstate, setStateを使って管理すれば良い)

つまりログインステータスなどをアプリケーション全体で一貫して管理するにはそこの層を導入する必要がある。

ReduxもしくはFluxがそこで登場するわけだが、自分の場合Reduxを選んだ。FacebookはFluxを使ってる/たとの話だ*1

しかしやはりそのためにReduxなりFluxを導入するのは欲する結果と得る複雑性の観点から合理的とは言い難く(個人の感想)、issueなどでもローカルステートの管理をしたいとの要望などは上がっていた

Relay Modernについて

Relay Modernでの改善については New in Relay Modern - Relay Docs に詳しいので再掲するようなことはしないが、今回Client Schema Extensionsなるものが入る。これはまさに上述の問題を解決するものだ。公式の説明にもそのように記述されている。

This should be able to replace some use cases that previously required a Flux/Redux store on the side.
https://facebook.github.io/relay/docs/new-in-relay-modern.html#client-schema-extensions より一部抜粋

そのほかに導入された改善として目につくのは、

の2つだ。前者はRelay Classicに慣れ親しんだものにとっては移行コストであって、新規の人への恩恵が大きい。学習コストが下がってるはずだ。後者については、自分のアプリでもそう遠くないうちに導入したい機能だと思っている。

Relay Modernへの移行について

※ ほとんど個人的な話

まぁ自分の場合いまのアプリケーションで差し迫って必要なのはローカルステート問題を解決する機能なので、そこがアップデートへのモチベーションとなる。 ところが、そのClient Schema Extensionsについて具体的な実装に触れたドキュメントは2017/04/23現在、存在しない。 似たようなドキュメント難民の声がIssueに上がっていた。

github.com

提示されたリンクからいくらかざっと辿ってみたけれど、@exportというディレクティブを使うんだ、metadatapropsに入るのかな?ということぐらいしかわからなかった。 それでも試してみようかと思ったけど、そもそもIssueに答えたFB中の人が

but getting this to work outside the FB environment hasn’t yet happened.
https://github.com/facebook/relay/issues/1656#issuecomment-296249276

と言っているので冒険にはまだ早いなと思いとどまった。もう少し合理的な努力ができそうになってからトライしよう。 そういう訳でまだアップデートしない。


*1:

2015/02/20 もう2年前の情報。2017年現在はどうなってるか知らない:

At Facebook, we have apps built entirely using Flux, entirely using Relay, or with both. One pattern we see emerging is letting Relay manage the bulk of the data flow for an application, but using Flux stores on the side to handle a subset of application state.

https://facebook.github.io/react/blog/2015/02/20/introducing-relay-and-graphql.html#how-does-it-relate-to-flux

Grammarly cm の曲

Youtubeで流れていいなと思った

Soul City の Here I come というやつらしい

↓ みたCM

www.youtube.com

調べてみるとMarmosetという音楽出版社で配信している。映像に合わせるBGM向けのライセンス提供が主な業態らしい。個人で試聴するぶんには無料でダウンロードできた。自分の映像にマッチするかちゃんと検証するためには全部聞けないとね、ということらしい。

ということで、結婚式の映像につけるとかpodcastで流すとか、作品や配信に使う人はライセンス契約してね。

曲のページ:

Marmoset // Here I Come by Soul City

ライセンス:

Marmoset // Licensing

ライセンスもクラウドファンディング向けとか、スモールビジネス向けだとか、色々あって面白い。

Marmoset:

www.marmosetmusic.com

広告:

English Grammar in Use Book with Answers and Interactive eBook: Self-Study Reference and Practice Book for Intermediate Learners of English

English Grammar in Use Book with Answers and Interactive eBook: Self-Study Reference and Practice Book for Intermediate Learners of English

React Navigationにコントリビュートした

React Navigation, APIはいい感じなんだけど現状beta版ということもあり、プロダクションで使っていこうと思うとなかなか機能が足りない。 それで2017/4/7現在では、v1リリースに向けて最低限必要な機能を実装していこうという状態にある。(と信じている)

github.com

その不足してる機能の一つが、「ネストされたルーターからナビゲーションをリセットできる」ことだ。現状はできない。 で、先日そのPRが出て無事マージされたんだけど、どうやら別の機能(setParams)がうまく動かなくなっていた。 ので、コードを変更しテストを書きPRを出したところ、無事マージしてもらえた。

github.com

よかった(感想)。

React Navigation は React Native本体のコアコントリビューターたちも絡んでいる。出したPRもFacebookでRNを作ってる中の人に見てもらえた。 そういう意味ではReact Native本体にPRを出していく肩慣らしとしてReact Navigationはちょうど良いプロジェクトかもしれない。 React NativeにPRを出すにはContributor License Agreement (CLA) にサインする必要があったりと多少敷居が高いが、 こちらは特にCLAにサインする必要もない。普通にPR送れば良い。のでみんなでコントリビュートしてv1リリースしよう。(ポジショントーク

あとがき

React Native本体はこのissueのコメントが役に立ってるぽいぐらいのことしかまだしてないので、今後もう少し折をみて寄与していけたらと思う。

Running Xcode's "analyze" on Yoga code surfaces issues · Issue #12609 · facebook/react-native · GitHub

おーい磯野〜 callback使ってる関数をPromiseでラップしてasync/awaitで書こうぜ〜

例1

const sleep = (ms) => {
   return new Promise(resolve => setTimeout(resolve, ms));
}

const something = async () => {
   await sleep(100)
   doSomething()
}

例2

Geolocation.getCurrentPosition() - Web API インターフェイス | MDN から

// 元メソッドの構文は   
// navigator.geolocation.getCurrentPosition(success, error, options)

// クラスの中

getCurrentPosition(options) {
  return new Promise((resolve, reject) => {
    navigator.geolocation.getCurrentPosition(resolve, reject, options)
  })
}

async something() {
  try {
    let position = await this.getCurrentPosition()
    this.doSomething(position)
  }
  catch(error) {
     console.log(error)
  }
}

例3

値を二つ返す系 例は React Nativeから Image コンポーネントgetSizeメソッド

// 元メソッドの構文: getSize(uri: string, success: function, failure?: function)
// クラスの中

_getSize(uri) {
  return new Promise((resolve, reject) => {
    Image.getSize(uri, (width, height) => { resolve({width, height}) }, reject)
  })
}

async something(imageUri) {
  try {
    let { width, height } = await this._getSize(imageUri)
    this.doSomething(width, height)
  }
  catch(error) {
     console.log(error)
  }
}

Relay: viewerとかrootにあるのよく見るけどあれアンチパターンらしいよ

関連しそうな記述があるものをとりあえずまとめておく。

Possible optimization related to setVariables and Node · Issue #1370 · facebook/relay · GitHub

Viewer ID and node interface implementation · Issue #1542 · facebook/relay · GitHub

Reactエレメント、コンポーネント、そしてインスタンス

React Components, Elements, and Instances - React Blog

を読んだメモ

エレメント(要素)

  • エレメントは、コンポーネントインスタンスまたはDOMノードを記述する、普通のオブジェクト
  • エレメントは、実際のインスタンスではない
  • エレメントは、何を画面上に見たいかReactに伝えるための手段(記述)
  • エレメントは、コンポーネントのタイプを表すtypeフィールドと、そのプロパティを表すpropsフィールドから成る*1
    • 子要素があれば、それも含むことになる
  • エレメントは、記述のためのイミュータブルなオブジェクトに過ぎない

DOMエレメントとコンポーネントエレメント

DOMエレメント

  • エレメントのtypeが文字列であるとき、エレメントはそのタグ名をもつDOMノードを表す。エレメントのpropsフィールドは、そのDOMノードの属性に対応する
  • 子要素も親要素も単なる記述に過ぎず、記述した時点では画面上のものへの参照を一切持たない
  • 画面上に見たいDOMを記述するエレメント

コンポーネントエレメント

DOMエレメントとコンポーネントエレメントの関係

  • Reactはコンポーネントエレメント(typeが関数もしくはクラスであるエレメント)を見つけると、そのコンポーネントが、与えられたpropsと共にどんなエレメントを吐き出すのかをたずねる
  • Reactはこのプロセスを、最終のDOMタグになるまで繰り返す

コンポーネント

画面に見たいものを記述すれば、そのインスタンスの管理(作成、更新、削除)はReactがよしなにやってくれる。

インスタンス

*1:説明の簡単のために省かれたが、実際はもう一つフィールドがある

React Nativeでのナビゲーションに便利なReact Navigation

2018-5-10追記: 先日v2も出ました。今目にしているブログ記事はだいぶ古いと思うので注意です。React Navigation (v2) · Routing and navigation for your React Native apps

2017/9/15追記: React Navigationは開発サイクルがしばらく停滞していたけれど、最近仕切り直しがあった。うまく行くといいな。https://reactnavigation.org/blog/2017/9/Renewed-v1

2017/3/17追記: beta-7時点で、ネストされたナビゲーター内からナビゲーター全体のstackをリセットできないという問題がある。ので、認証してトップに戻るとか、アップロード完了したらトップに戻るとかまともにできないと思うので辛そう。Specify reset key to define what state should be reset · Issue #691 · react-navigation/react-navigation · GitHub

2017/3/15追記: 記事中で「ナビゲーションライブラリは収束しつつある模様」という旨のことを書いたけれども、昨日Airbnbからナビゲーションライブラリが発表されたりと、依然動きはある。ExNavigationとNavigationExperimentalはReact Navigationに後続したというのが観測範囲。*1 *2


React Nativeでのナビゲーションは、標準のコンポーネントNavigation)だと辛いものがあった。 そこでいくつか他の実装が出てきてたのだけど(ExNavigationとか)、最近はReact Navigation (v2) · Routing and navigation for your React Native appsに収束しつつある模様。 手元のプロジェクトでも使っているけど、見通しが良いコードを書けたのでかなり満足。

Navigationでの辛さを解決するための、Navigation Experimentalというその名の通り実験的なコンポーネントも一応React Native内で提供されていたけど、0.43.0からはDeprecatedになる。公式でも React Navigation 推しになるからだ。

github.com

React Nativeであれば、index.ios.jsで以下のようにコンポーネントを読み込んで、ルーティングを書いてやればよい。

import {
  StackNavigator,
} from 'react-navigation';

const BasicApp = StackNavigator({
  Main: {screen: MainScreen},
  Profile: {screen: ProfileScreen},
});

他のスクリーン(コンポーネント)内から目的のスクリーンに遷移する際は以下のようにnavigate('Profile', { name: 'Jane' })と、遷移先のKeyと、必要に応じてプロパティをメソッドに渡す。

class MainScreen extends React.Component {
  static navigationOptions = {
    title: 'Welcome',
  };
  render() {
    const { navigate } = this.props.navigation;
    return (
      <Button
        title="Go to Jane's profile"
        onPress={() =>
          navigate('Profile', { name: 'Jane' })
        }
      />
    );
  }
}

なお上記の例はReact Navigation (v2) · Routing and navigation for your React Native appsからそのまま拝借したもの。

懸念があるとすれば、2017/3/9現在のバージョンがv1.0.0-beta.6であることだ。個人的にもそこにためらいがあったものの、試しに導入して見たところ手元のユースケースの範囲では特段困ることはなかったので導入してしまった(計9スクリーン、TabとStackのネスト。そこまで大きくない)。得られる恩恵が大きすぎた。

また、React Nativeコミュニティが関わっているプロジェクトに対する信頼もある。レポジトリやコミュニティを眺める限り、Breaking changesがあればそれ相応のマイグレーションのための対応がされるはずだと信じている。

そういえば、先日のReact Native Team AMA Liveでも、これからReact Native始めるなら現時点ではReact Navigationを見るといいって言及されてたっけな。

www.facebook.com

6:14秒あたりから。そのちょっと前には、Reactでwebアプリ書き始めるならReact Router使うといいよ、って言ってる。

React Navigation (v2) · Routing and navigation for your React Native apps

関連

書いた: メモ: React Navigation を使い始める時に確認すると良さそうなこと (v1.0.0-beta.10時点) - taiki-t's diary


*1:https://reactnavigation.org/blog/2017/1/Introducing-React-Navigation には次のように記載されている。

It replaces and improves upon several navigation libraries in the ecosystem, including Ex-Navigation and React Native's Navigator and NavigationExperimental components.

GitHub - expo/ex-navigation: Route-centric navigation for React Native には

The successor to ExNavigation, "react-navigation", is now in public beta.

NavigationExperimentalについては冒頭のdeprecation commitより

The NavigationExperimental views live on in the React-Navigation project,

*2:他、Airbnb含め目についたナビゲーションライブラリ: