taiki-t's diary

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

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)
  }
}

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含め目についたナビゲーションライブラリ:

React NativeでiOSのランディングスクリーンに画像を設定する方法

という記事を書いた。

taiki-t.hatenablog.com

だいたい図にした。constrainsの設定の時だけ、対象のオブジェクトからviewへcontrol + click してドラッグというのがわかりにくいかも。