taiki-t's diary

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

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 してドラッグというのがわかりにくいかも。

Presigned post to aws s3 from React Native

It took a longtime to figure out how to upload images to aws s3 using presigned post from React Native.

The key part is to use FormData to construct a payload and post it with POST http method. PUTing FormData payload with pre-signed URL didn’t work for me at least.

Server Side

I use Ruby on Rails for server side here. Read Direct to S3 Image Uploads in Rails | Heroku Dev Center to fully understand the code below.

FYI: You don’t need to setup CORS settings in the article for native apps.

The security model for XMLHttpRequest is different than on web as there is no concept of CORS in native apps. - https://facebook.github.io/react-native/docs/network.html

Here is the code:

# In your controller
def presigned_url
  options = {
    key: "uploads/photos/#{SecureRandom.uuid}/${filename}",
    success_action_status: '201',
    acl: 'public-read'
  }
  presigned_post = S3_BUCKET.presigned_post(options)

  # FIXME: http://stackoverflow.com/a/36941996/2930161
  if presigned_post
    render plain: { presignedPost: { fields: presigned_post.fields, url: presigned_post.url } }.to_json, status: 200, content_type: 'application/json'
  else
    render plain: { error: 'No presigned urls.' }.to_json, status: 422, content_type: 'application/json'
  end
end

React Native

// In your component.
async _uploadToS3() {
  try {
    // implement your method to fetch a presigned post object.
    const presignedPost = await this._fetchPreSignedPost()

    let formData = new FormData();

    Object.keys(presignedPost.fields).forEach((key) => {
      formData.append(key, presignedPost.fields[key]);
    });

    formData.append('file', {
      // Provide an url of a target image.
      uri: this.image.uri,
      type: 'image/jpeg',
      name: 'image.jpg',
    })

    const presignedUrl = presignedPost.url

    fetch(presignedUrl, { method: 'POST', body: formData, headers: { 'Content-Type': 'multipart/form-data'} })
  }
  catch (error) {
    console.log(error)
  }
}

This Answer on StackOverflow inspired me for the code, thanks:

stackoverflow.com

Other Links

http://docs.aws.amazon.com/sdkforruby/api/Aws/S3/Bucket.html#presigned_post-instance_method

http://docs.aws.amazon.com/sdkforruby/api/Aws/S3/PresignedPost.html

Upload an Object Using a Pre-Signed URL (AWS SDK for Ruby) - Amazon Simple Storage Service

react-native/XHRExampleFormData.js at master · facebook/react-native · GitHub