メインコンテンツにスキップ

「エンジニアリング」タグの付いた投稿26件

すべてのタグを表示

Create React Native Appの導入

·2分で読めます
Adam Perry
Expoのソフトウェアエンジニア

本日、React Nativeプロジェクトを始めるのを大幅に簡単にする新しいツールであるCreate React Native Appを発表します! これはCreate React Appのデザインに大きく影響を受けており、FacebookExpo(旧Exponent)のコラボレーションの成果です。

多くの開発者は、特にAndroidの場合、React Nativeの現在のネイティブビルド依存関係のインストールと構成に苦労しています。Create React Native Appを使用すると、XcodeやAndroid Studioを使用する必要がなくなり、LinuxまたはWindowsを使用してiOSデバイス向けに開発できます。これは、ネイティブコードをコンパイルせずに、純粋なJavaScriptで記述されたCRNAプロジェクトをロードして実行するExpoアプリを使用して実現されます。

新しいプロジェクトを作成してみましょう(yarnがインストールされている場合は、適切なyarnコマンドに置き換えてください)

$ npm i -g create-react-native-app
$ create-react-native-app my-project
$ cd my-project
$ npm start

これにより、React Nativeパッカーが起動し、QRコードが出力されます。Expoアプリで開いてJavaScriptをロードします。console.logの呼び出しはターミナルに転送されます。標準のReact Native APIとExpo SDKを利用できます。

ネイティブコードはどうですか?

多くのReact Nativeプロジェクトには、コンパイルする必要のあるJavaまたはObjective-C/Swiftの依存関係があります。Expoアプリには、カメラ、ビデオ、連絡先などのAPIが含まれており、Airbnbのreact-native-mapsFacebook認証のような一般的なライブラリがバンドルされています。ただし、Expoがバンドルしていないネイティブコードの依存関係が必要な場合は、おそらく独自のビルド構成が必要になります。Create React Appと同様に、CRNAでは「ejecting」がサポートされています。

npm run ejectを実行すると、react-native initで生成されるものと非常によく似たプロジェクトが得られます。その時点で、react-native initで開始した場合と同じようにXcodeやAndroid Studioが必要になり、react-native linkでライブラリを追加できるようになり、ネイティブコードのコンパイルプロセスを完全に制御できるようになります。

質問はありますか?フィードバックは?

Create React Native Appは、一般的な使用に十分なほど安定しており、使用した感想をぜひお聞かせください!Twitterで私を見つけるか、GitHubリポジトリで問題をオープンしてください。プルリクエストは大歓迎です!

Animatedにネイティブドライバーを使用する

·7分で読めます
Janic Duplessis
App & Flowのソフトウェアエンジニア

この1年間、Animatedライブラリを使用するアニメーションのパフォーマンスを向上させることに取り組んできました。アニメーションは美しいユーザーエクスペリエンスを作成するために非常に重要ですが、正しく行うのは難しい場合もあります。開発者が、コードの一部が遅延を引き起こすことを心配せずに、パフォーマンスの高いアニメーションを簡単に作成できるようにしたいと考えています。

これは何ですか?

Animated APIは、シリアライズ可能であるという非常に重要な制約を念頭に置いて設計されました。これは、アニメーションに関するすべての情報を開始する前にネイティブに送信できることを意味し、ネイティブコードがすべてのフレームでブリッジを介さずにUIスレッドでアニメーションを実行できるようにします。アニメーションが開始されると、JSスレッドがブロックされ、アニメーションがスムーズに実行されるため、非常に便利です。実際には、ユーザーコードがJSスレッドで実行され、ReactのレンダリングもJSを長時間ロックする可能性があるため、これは頻繁に発生する可能性があります。

少し歴史を...

このプロジェクトは、ExpoがAndroidでli.stアプリを構築した約1年前に始まりました。Krzysztof Magieraが、Androidでの初期実装を構築するために契約されました。最終的にはうまく機能し、li.stはAnimatedを使用してネイティブ駆動のアニメーションを搭載した最初のアプリになりました。数か月後、Brandon WithrowがiOSでの初期実装を構築しました。その後、Ryan Gombaと私が、Animated.eventのサポートなどの不足している機能を追加し、本番アプリで使用する際に発見したバグを修正しました。これは真のコミュニティの努力であり、開発の大部分を後援してくれたExpoだけでなく、関わったすべての人に感謝したいと思います。現在、React NativeのTouchableコンポーネントや、新しくリリースされたReact Navigationライブラリのナビゲーションアニメーションに使用されています。

どのように機能しますか?

まず、JSドライバーを使用してAnimatedを使用した場合のアニメーションの現在の動作を確認しましょう。Animatedを使用する場合、実行するアニメーションを表すノードのグラフを宣言し、ドライバーを使用して事前定義されたカーブを使用してAnimated値を更新します。Animated.eventを使用して、Viewのイベントに接続することでAnimated値を更新することもできます。

アニメーションのステップと、どこで発生するかを以下に示します

  • JS: アニメーションドライバは、requestAnimationFrame を使用して各フレームで実行し、アニメーションカーブに基づいて計算された新しい値を使用して、駆動する値を更新します。
  • JS: 中間値が計算され、View にアタッチされた props ノードに渡されます。
  • JS: ViewsetNativeProps を使用して更新されます。
  • JS からネイティブブリッジへ。
  • ネイティブ: UIView または android.View が更新されます。

ご覧のとおり、ほとんどの作業は JS スレッドで実行されます。ブロックされると、アニメーションはフレームをスキップします。また、ネイティブビューを更新するために、フレームごとに JS からネイティブへのブリッジを通過する必要があります。

ネイティブドライバが行うことは、これらのステップすべてをネイティブに移動することです。Animated はアニメーションノードのグラフを生成するため、アニメーションの開始時に一度だけシリアル化してネイティブに送信できます。これにより、JS スレッドへのコールバックの必要性がなくなります。ネイティブコードは、各フレームで UI スレッド上のビューの更新を直接処理できます。

アニメーション化された値と補間ノードをシリアル化する方法の例を次に示します (正確な実装ではなく、単なる例です)。

ネイティブの値ノードを作成します。これはアニメーション化される値です。

NativeAnimatedModule.createNode({
id: 1,
type: 'value',
initialValue: 0,
});

ネイティブの補間ノードを作成します。これは、ネイティブドライバに値を補間する方法を指示します。

NativeAnimatedModule.createNode({
id: 2,
type: 'interpolation',
inputRange: [0, 10],
outputRange: [10, 0],
extrapolate: 'clamp',
});

ネイティブの props ノードを作成します。これは、ネイティブドライバにアタッチされているビューのどの prop であるかを指示します。

NativeAnimatedModule.createNode({
id: 3,
type: 'props',
properties: ['style.opacity'],
});

ノードを相互に接続します。

NativeAnimatedModule.connectNodes(1, 2);
NativeAnimatedModule.connectNodes(2, 3);

props ノードをビューに接続します。

NativeAnimatedModule.connectToView(3, ReactNative.findNodeHandle(viewRef));

これにより、ネイティブアニメーションモジュールは、JS に移動して値を計算することなく、ネイティブビューを直接更新するために必要なすべての情報を取得します。

残っているのは、目的のアニメーションカーブのタイプと、更新するアニメーション値を指定して、アニメーションを実際に開始することだけです。タイミングアニメーションは、JS でアニメーションのすべてのフレームを事前に計算することで簡略化し、ネイティブ実装を小さくすることもできます。

NativeAnimatedModule.startAnimation({
type: 'timing',
frames: [0, 0.1, 0.2, 0.4, 0.65, ...],
animatedValueId: 1,
});

次に、アニメーションの実行時に何が起こるかの内訳を示します。

  • ネイティブ: ネイティブアニメーションドライバは、CADisplayLink または android.view.Choreographer を使用して各フレームで実行し、アニメーションカーブに基づいて計算された新しい値を使用して、駆動する値を更新します。
  • ネイティブ: 中間値が計算され、ネイティブビューにアタッチされた props ノードに渡されます。
  • ネイティブ: UIView または android.View が更新されます。

ご覧のとおり、JS スレッドはもうなくなり、ブリッジももうありません。つまり、アニメーションが高速化されます! 🎉🎉

アプリでこれを使用するにはどうすればよいですか?

通常のアニメーションの場合、答えは簡単です。アニメーションの開始時に、アニメーション構成に useNativeDriver: true を追加するだけです。

以前

Animated.timing(this.state.animatedValue, {
toValue: 1,
duration: 500,
}).start();

以降

Animated.timing(this.state.animatedValue, {
toValue: 1,
duration: 500,
useNativeDriver: true, // <-- Add this
}).start();

アニメーション化された値は、1つのドライバとのみ互換性があるため、値のアニメーションを開始するときにネイティブドライバを使用する場合は、その値のすべてのアニメーションもネイティブドライバを使用するようにしてください。

Animated.event でも動作します。これは、スクロール位置に従う必要のあるアニメーションがある場合に非常に役立ちます。ネイティブドライバがない場合、React Native の非同期の性質のため、常にジェスチャの 1 フレーム遅れて実行されるからです。

以前

<ScrollView
scrollEventThrottle={16}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: this.state.animatedValue } } }]
)}
>
{content}
</ScrollView>

以降

<Animated.ScrollView // <-- Use the Animated ScrollView wrapper
scrollEventThrottle={1} // <-- Use 1 here to make sure no events are ever missed
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: this.state.animatedValue } } }],
{ useNativeDriver: true } // <-- Add this
)}
>
{content}
</Animated.ScrollView>

注意事項

Animated でできるすべてのことが、現在ネイティブアニメーションでサポートされているわけではありません。主な制限は、レイアウト以外のプロパティのみをアニメーション化できることです。transformopacity のようなものは機能しますが、Flexbox や位置プロパティは機能しません。もう 1 つは、Animated.event では、バブリングイベントではなく、直接イベントでのみ動作することです。つまり、PanResponder では動作しませんが、ScrollView#onScroll のようなものでは動作します。

ネイティブアニメーションは、かなり前から React Native の一部でしたが、実験的と見なされていたため、ドキュメント化されていませんでした。そのため、この機能を使用する場合は、React Native の最新バージョン (0.40 以上) を使用していることを確認してください。

リソース

アニメーションの詳細については、この講演Christopher Chedeau が行ったものをご覧になることをお勧めします。

アニメーションと、ネイティブにオフロードすることでユーザーエクスペリエンスを向上できる方法について深く掘り下げたい場合は、この講演Krzysztof Magiera が行ったものがあります。

React Nativeアプリの右から左へのレイアウトのサポート

·7分で読めます
Mengjue (Mandy) Wang
Facebook のソフトウェアエンジニアインターン

アプリをアプリストアに公開した後、国際化は、オーディエンスのリーチをさらに拡大するための次のステップです。20 か国以上、世界中の多くの人々が右から左 (RTL) の言語を使用しています。したがって、アプリで RTL をサポートすることは必須です。

React Native が RTL レイアウトをサポートするように改善されたことを発表します。これは現在、react-native の master ブランチで利用可能であり、次の RC: v0.33.0-rc で利用可能になります。

これには、RN が使用するコアレイアウトエンジンである css-layout と、RN コア実装、および RTL をサポートするための特定の OSS JS コンポーネントの変更が含まれていました。

本番環境で RTL サポートをテストするために、最新バージョンの Facebook Ads Manager アプリ (最初のクロスプラットフォーム 100% RN アプリ) が、iOS および Android の両方で RTL レイアウトを備えたアラビア語とヘブライ語で利用可能になりました。RTL 言語では次のようになります。

RTL サポートのための RN の変更の概要

css-layout には、レイアウトに startend の概念がすでにあります。左から右 (LTR) のレイアウトでは、startleft を意味し、endright を意味します。ただし、RTL では、startright を意味し、endleft を意味します。これは、RN が startend の計算に依存して、positionpaddingmargin を含む正しいレイアウトを計算できることを意味します。

さらに、css-layout では、各コンポーネントの方向がその親から継承されます。これは、ルートコンポーネントの方向を RTL に設定するだけで、アプリ全体が反転することを意味します。

次の図は、変更点を大まかに説明しています。

これらには次のものが含まれます。

この更新により、アプリの RTL レイアウトを許可すると、

  • すべてのコンポーネントレイアウトが水平方向に反転します
  • RTL 対応の OSS コンポーネントを使用している場合、一部のジェスチャとアニメーションは自動的に RTL レイアウトになります
  • アプリを完全に RTL 対応にするために必要な追加作業は最小限である可能性があります

アプリを RTL 対応にする

  1. RTL をサポートするには、まずアプリに RTL 言語バンドルを追加する必要があります。

    • iOSAndroid の一般的なガイドを参照してください。
  2. ネイティブコードの先頭で allowRTL() 関数を呼び出して、アプリの RTL レイアウトを許可します。このユーティリティは、アプリの準備が整ったときにのみ RTL レイアウトに適用するために提供しました。次に例を示します。

    iOS

    // in AppDelegate.m
    [[RCTI18nUtil sharedInstance] allowRTL:YES];

    Android

    // in MainActivity.java
    I18nUtil sharedI18nUtilInstance = I18nUtil.getInstance();
    sharedI18nUtilInstance.allowRTL(context, true);
  3. Android の場合、<application> 要素に android:supportsRtl="true"AndroidManifest.xml ファイルに追加する必要があります。

これで、アプリを再コンパイルしてデバイスの言語を RTL 言語 (アラビア語やヘブライ語など) に変更すると、アプリのレイアウトが自動的に RTL に変更されるはずです。

RTL 対応のコンポーネントの作成

一般に、ほとんどのコンポーネントはすでに RTL 対応です。次に例を示します。

  • 左から右のレイアウト
  • 右から左のレイアウト

ただし、注意すべき点がいくつかあり、それらについては I18nManager が必要になります。I18nManager には、アプリのレイアウトが RTL かどうかを判断する定数 isRTL があり、それに応じて必要な変更を行うことができます。

方向の意味を持つアイコン

コンポーネントにアイコンまたは画像がある場合、RN はソース画像を反転しないため、LTR および RTL レイアウトで同じように表示されます。したがって、レイアウトスタイルに応じて反転する必要があります。

  • 左から右のレイアウト
  • 右から左のレイアウト

次に、方向に応じてアイコンを反転する 2 つの方法を示します。

  • 画像コンポーネントに transform スタイルを追加します

    <Image
    source={...}
    style={{transform: [{scaleX: I18nManager.isRTL ? -1 : 1}]}}
    />
  • または、方向に応じて画像ソースを変更します

    let imageSource = require('./back.png');
    if (I18nManager.isRTL) {
    imageSource = require('./forward.png');
    }
    return <Image source={imageSource} />;

ジェスチャーとアニメーション

AndroidおよびiOSの開発において、レイアウトをRTL(右から左)に変更すると、ジェスチャーとアニメーションはLTR(左から右)レイアウトとは逆になります。現在、RNでは、ジェスチャーとアニメーションはRNコアコードレベルではなく、コンポーネントレベルでサポートされています。幸いなことに、これらのコンポーネントの一部は、SwipeableRowNavigationExperimentalなど、すでにRTLをサポートしています。ただし、ジェスチャーを使用する他のコンポーネントは、手動でRTLをサポートする必要があります。

ジェスチャーのRTLサポートを説明する良い例として、SwipeableRowが挙げられます。

ジェスチャーの例
// SwipeableRow.js
_isSwipingExcessivelyRightFromClosedPosition(gestureState: Object): boolean {
// ...
const gestureStateDx = IS_RTL ? -gestureState.dx : gestureState.dx;
return (
this._isSwipingRightFromClosed(gestureState) &&
gestureStateDx > RIGHT_SWIPE_THRESHOLD
);
},
アニメーションの例
// SwipeableRow.js
_animateBounceBack(duration: number): void {
// ...
const swipeBounceBackDistance = IS_RTL ?
-RIGHT_SWIPE_BOUNCE_BACK_DISTANCE :
RIGHT_SWIPE_BOUNCE_BACK_DISTANCE;
this._animateTo(
-swipeBounceBackDistance,
duration,
this._animateToClosedPositionDuringBounce,
);
},

RTL対応アプリの維持

RTL対応アプリの初回リリース後でも、新機能の反復処理が必要になる可能性があります。開発効率を向上させるために、I18nManagerは、テストデバイスの言語を変更せずにRTLテストを高速化するためのforceRTL()関数を提供します。アプリ内でこれに対するシンプルなスイッチを提供することもできます。以下は、RNTesterのRTLの例からの例です。

<RNTesterBlock title={'Quickly Test RTL Layout'}>
<View style={styles.flexDirectionRow}>
<Text style={styles.switchRowTextView}>forceRTL</Text>
<View style={styles.switchRowSwitchView}>
<Switch
onValueChange={this._onDirectionChange}
style={styles.rightAlignStyle}
value={this.state.isRTL}
/>
</View>
</View>
</RNTesterBlock>;

_onDirectionChange = () => {
I18nManager.forceRTL(!this.state.isRTL);
this.setState({isRTL: !this.state.isRTL});
Alert.alert(
'Reload this page',
'Please reload this page to change the UI direction! ' +
'All examples in this app will be affected. ' +
'Check them out to see what they look like in RTL layout.',
);
};

新しい機能に取り組む際、このボタンを簡単に切り替えてアプリをリロードすることで、RTLレイアウトを確認できます。利点は、テストのために言語設定を変更する必要がないことですが、次のセクションで説明するように、一部のテキストの配置は変更されません。したがって、アプリをリリースする前にRTL言語でテストすることを常にお勧めします。

制限事項と今後の計画

RTLサポートはアプリのUXの大部分をカバーする必要がありますが、今のところいくつかの制限があります。

  • テキストの配置の動作はAndroidとiOSで異なります
    • iOSでは、デフォルトのテキストの配置はアクティブな言語バンドルに依存し、常に片側に配置されます。Androidでは、デフォルトのテキストの配置はテキストコンテンツの言語に依存します。つまり、英語は左揃え、アラビア語は右揃えになります。
    • 理論的には、これはプラットフォーム間で一貫性を持たせる必要がありますが、アプリを使用する際に一方の動作を好む人もいるかもしれません。テキストの配置に関するベストプラクティスを見つけるためには、さらなるユーザーエクスペリエンス調査が必要になる可能性があります。
  • 「真の」左/右は存在しません

    前述したように、JS側のleft/rightスタイルをstart/endにマッピングします。RTLレイアウトの場合、コード内のすべてのleftは画面上では「右」になり、コード内のrightは画面上では「左」になります。製品コードをあまり変更する必要がないため便利ですが、コード内で「真の左」または「真の右」を指定する方法がないことを意味します。将来的には、言語に関係なくコンポーネントがその方向を制御できるようにすることが必要になる可能性があります。

  • ジェスチャーとアニメーションのRTLサポートをより開発者フレンドリーにする

    現在、ジェスチャーとアニメーションをRTL対応にするには、まだいくつかのプログラミング作業が必要です。将来的には、ジェスチャーとアニメーションのRTLサポートをより開発者フレンドリーにする方法を見つけることが理想的です。

試してみましょう!

RTLサポートの詳細については、RNTesterRTLExampleを参照し、その動作を教えてください!

最後に、お読みいただきありがとうございます!React NativeのRTLサポートが、国際的なオーディエンス向けにアプリを成長させるのに役立つことを願っています!

React Nativeパフォーマンスの詳細

·2分で読めます
Pieter De Baets
Facebookのソフトウェアエンジニア

React Nativeを使用すると、ReactとRelayの宣言型プログラミングモデルを使用して、JavaScriptでAndroidおよびiOSアプリを構築できます。これにより、より簡潔で理解しやすいコード、コンパイルサイクルなしでの高速な反復処理、および複数のプラットフォーム間でのコードの簡単な共有が可能になります。より迅速にリリースでき、アプリの見た目と操作感を素晴らしいものにする上で本当に重要な細部に集中できます。パフォーマンスの最適化は、この大きな部分です。ここでは、React Nativeアプリの起動を2倍高速化した経緯について説明します。

なぜ急ぐのか?

アプリの実行速度が速いほど、コンテンツの読み込みが速くなり、人々が操作する時間が長くなり、スムーズなアニメーションによりアプリの使い心地が向上します。 2011年クラスの携帯電話2Gネットワークで多数を占める新興市場では、パフォーマンスに焦点を当てることで、アプリが使用可能なものとそうでないものとの違いが生じる可能性があります。

React NativeをiOSAndroidでリリースして以来、リストビューのスクロールパフォーマンス、メモリ効率、UI応答性、アプリの起動時間の改善に取り組んできました。起動はアプリの第一印象を決定し、フレームワークのすべての部分にストレスを与えるため、取り組む上で最もやりがいがあり、難しい問題です。

これは抜粋です。投稿の残りの部分はFacebook Codeでご覧ください。

ホットリローディングの導入

·9分で読めます
Martín Bigio
Instagramのソフトウェアエンジニア

React Nativeの目標は、可能な限り最高の開発者エクスペリエンスを提供することです。その大きな部分は、ファイルを保存してから変更を確認できるようになるまでの時間です。私たちの目標は、アプリが成長しても、このフィードバックループを1秒未満にすることです。

この理想に近づくことができたのは、主に3つの機能によるものです。

  • 言語としてJavaScriptを使用すると、コンパイルサイクル時間が長くなりません。
  • es6/flow/jsxファイルをVMが理解できる通常のJavaScriptに変換するPackagerと呼ばれるツールを実装します。これは、高速な増分変更を可能にするためにメモリに中間状態を保持し、複数のコアを使用するサーバーとして設計されました。
  • 保存時にアプリをリロードするライブリロードと呼ばれる機能を構築します。

この時点で、開発者にとってのボトルネックは、アプリをリロードするのにかかる時間ではなく、アプリの状態が失われることです。一般的なシナリオは、起動画面から複数の画面離れた場所にある機能に取り組むことです。リロードするたびに、同じパスを何度もクリックして機能に戻る必要があり、サイクルが数秒長くなります。

ホットリロード

ホットリロードの背後にあるアイデアは、アプリを実行したまま、実行時に編集したファイルの新しいバージョンを挿入することです。これにより、特にUIを調整している場合に非常に便利な状態を失うことはありません。

百聞は一見にしかずです。ライブリロード(現在)とホットリロード(新規)の違いをご覧ください。

よく見ると、レッドボックスから回復できること、以前になかったモジュールをフルリロードせずにインポートを開始できることに気付くはずです。

警告: JavaScriptは非常にステートフルな言語であるため、ホットリロードを完全に実装することはできません。実際には、現在のセットアップは、通常のユースケースの大部分でうまく機能しており、何かが混乱した場合に備えて、フルリロードが常に利用可能であることがわかりました。

ホットリロードは0.22から利用可能になり、有効にすることができます

  • 開発者メニューを開きます
  • 「ホットリロードを有効にする」をタップします

要するに実装

これで、必要な理由と使用方法がわかったので、楽しい部分が始まります:実際にどのように機能するのか。

ホットリロードは、Hot Module ReplacementまたはHMRと呼ばれる機能に基づいて構築されています。これは最初にwebpackによって導入され、React Native Packager内に実装しました。 HMRにより、Packagerはファイルの変更を監視し、アプリに含まれるシンHMRランタイムにHMRアップデートを送信します。

要するに、HMRアップデートには、変更されたJSモジュールの新しいコードが含まれています。ランタイムがそれらを受け取ると、古いモジュールのコードを新しいコードに置き換えます

HMRアップデートには、変更したいモジュールのコードだけよりも少し多くの情報が含まれています。これは、ランタイムが変更を検出するには、それを置き換えるだけでは不十分なためです。問題は、モジュールシステムが更新したいモジュールのエクスポートをすでにキャッシュしている可能性があることです。たとえば、次の2つのモジュールで構成されるアプリがあるとします。

// log.js
function log(message) {
const time = require('./time');
console.log(`[${time()}] ${message}`);
}

module.exports = log;
// time.js
function time() {
return new Date().getTime();
}

module.exports = time;

モジュールlogは、モジュールtimeによって提供された現在の日付を含む、提供されたメッセージを出力します。

アプリがバンドルされると、React Nativeは__d関数を使用して、各モジュールをモジュールシステムに登録します。このアプリの場合、多くの__d定義の中で、logの定義が1つあります。

__d('log', function() {
... // module's code
});

この呼び出しは、各モジュールのコードを、通常はファクトリ関数と呼ばれる匿名関数にラップします。モジュールシステムランタイムは、各モジュールのファクトリ関数、それがすでに実行されているかどうか、およびそのような実行の結果(エクスポート)を追跡します。モジュールが必要な場合、モジュールシステムはすでにキャッシュされているエクスポートを提供するか、モジュールのファクトリ関数を初めて実行して結果を保存します。

そこで、アプリを起動してlogを必要とするとします。この時点で、logtimeのファクトリ関数のどちらも実行されていないため、エクスポートはキャッシュされていません。次に、ユーザーはtimeを変更して、MM/DDで日付を返すようにします。

// time.js
function bar() {
const date = new Date();
return `${date.getMonth() + 1}/${date.getDate()}`;
}

module.exports = bar;

Packagerは、`time`の新しいコードをランタイムに送信し(ステップ1)、`log`が最終的にrequireされたときに、エクスポートされた関数が実行され、その際`time`の変更が反映されます(ステップ2)。

ここで、`log`のコードがトップレベルのrequireとして`time`を必要とする場合を考えます。

const time = require('./time'); // top level require

// log.js
function log(message) {
console.log(`[${time()}] ${message}`);
}

module.exports = log;

`log`がrequireされると、ランタイムはそのエクスポートと`time`のエクスポートをキャッシュします(ステップ1)。その後、`time`が変更された場合、HMRプロセスは、単に`time`のコードを置き換えただけで完了することはできません。もしそうしてしまうと、`log`が実行されるときに、キャッシュされた`time`のコピー(古いコード)で実行されることになります。

`log`が`time`の変更を反映するためには、依存関係にあるモジュールがホットスワップされたため、キャッシュされたエクスポートをクリアする必要があります(ステップ3)。最後に、`log`が再度requireされると、そのファクトリ関数が実行され、`time`をrequireし、新しいコードを取得します。

HMR API

React NativeにおけるHMRは、`hot`オブジェクトを導入することでモジュールシステムを拡張します。このAPIは、webpackのAPIに基づいています。`hot`オブジェクトは、モジュールをホットスワップする必要があるときに実行されるコールバックを定義できる`accept`という関数を公開します。たとえば、`time`のコードを次のように変更すると、`time`を保存するたびに、コンソールに「time changed」と表示されます。

// time.js
function time() {
... // new code
}

module.hot.accept(() => {
console.log('time changed');
});

module.exports = time;

このAPIを手動で使用する必要があるのはまれなケースのみです。ほとんどの一般的なユースケースでは、ホットリローディングはすぐに機能するはずです。

HMRランタイム

前述したように、ホットスワップされるモジュールを使用するモジュールが既に実行されてインポートがキャッシュされている場合、HMR更新を受け入れるだけでは不十分な場合があります。たとえば、映画アプリの例の依存関係ツリーに、前の例の`log`と`time`モジュールに依存する`MovieSearch`と`MovieScreen`ビューに依存するトップレベルの`MovieRouter`があるとします。

ユーザーが映画の検索ビューにアクセスしたが、もう一方のビューにはアクセスしなかった場合、`MovieScreen`を除くすべてのモジュールはエクスポートをキャッシュしていることになります。モジュール`time`に変更を加えると、ランタイムは`time`の変更を反映するために`log`のエクスポートをクリアする必要があります。プロセスはそこで終わりません。ランタイムは、すべての親が受け入れられるまで、このプロセスを再帰的に繰り返します。そのため、`log`に依存するモジュールを取得し、それらを受け入れようとします。`MovieScreen`については、まだrequireされていないため、そこで中断できます。`MovieSearch`については、エクスポートをクリアし、親を再帰的に処理する必要があります。最後に、`MovieRouter`についても同じ処理を行い、依存するモジュールがないため、そこで完了します。

依存関係ツリーをたどるために、ランタイムは、HMR更新時にPackagerから逆依存関係ツリーを受け取ります。この例では、ランタイムは次のようなJSONオブジェクトを受け取ります。

{
modules: [
{
name: 'time',
code: /* time's new code */
}
],
inverseDependencies: {
MovieRouter: [],
MovieScreen: ['MovieRouter'],
MovieSearch: ['MovieRouter'],
log: ['MovieScreen', 'MovieSearch'],
time: ['log'],
}
}

Reactコンポーネント

Reactコンポーネントは、ホットリローディングを機能させるのが少し困難です。問題は、コンポーネントの状態が失われるため、古いコードを新しいコードに単純に置き換えることができないことです。ReactのWebアプリケーションの場合、Dan Abramov氏は、この問題を解決するためにwebpackのHMR APIを使用するbabelのトランスフォームを実装しました。簡単に言うと、彼のソリューションは、*トランスフォーム時*にすべてのReactコンポーネントのプロキシを作成することによって機能します。プロキシはコンポーネントの状態を保持し、ライフサイクルメソッドを実際のコンポーネントに委任します。これはホットリロードするものです。

プロキシコンポーネントの作成に加えて、トランスフォームは、Reactにコンポーネントを再レンダリングさせるコードを含む`accept`関数も定義します。このようにして、アプリの状態を失うことなく、レンダリングコードをホットリロードできます。

React Nativeに付属しているデフォルトのトランスフォーマーは、`babel-preset-react-native`を使用します。これは、webpackを使用するReact Webプロジェクトで使用する場合と同じ方法で、`react-transform`を使用するように構成されています。

Reduxストア

Reduxストアでホットリローディングを有効にするには、webpackを使用するWebプロジェクトで行うのと同様に、HMR APIを使用するだけです。

// configureStore.js
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import reducer from '../reducers';

export default function configureStore(initialState) {
const store = createStore(
reducer,
initialState,
applyMiddleware(thunk),
);

if (module.hot) {
module.hot.accept(() => {
const nextRootReducer = require('../reducers/index').default;
store.replaceReducer(nextRootReducer);
});
}

return store;
};

リデューサーを変更すると、そのリデューサーを受け入れるコードがクライアントに送信されます。次に、クライアントは、リデューサーが自分自身を受け入れる方法を知らないことに気付くため、それを参照するすべてのモジュールを検索して受け入れようとします。最終的に、フローは単一のストアである`configureStore`モジュールに到達し、HMR更新を受け入れます。

結論

ホットリローディングの改善に協力したい場合は、Dan Abramovのホットリローディングの将来に関する投稿を読んで、貢献することをお勧めします。たとえば、Johny Daysは複数の接続されたクライアントで動作するようにする予定です。この機能を維持し、改善するために、皆様のご協力をお願いいたします。

React Nativeでは、優れた開発者エクスペリエンスを実現するために、アプリの構築方法を再考する機会があります。ホットリローディングはそのパズルの一部にすぎません。さらに改善するために、他にどのような斬新なハックが可能でしょうか。

React Nativeアプリをアクセシブルにする

·2分で読めます
Georgiy Kassabli
Facebookのソフトウェアエンジニア

Web上のReactとモバイル上のReact Nativeの最近の立ち上げに伴い、開発者が製品を構築するための新しいフロントエンドフレームワークを提供しました。堅牢な製品を構築する上で重要な側面の1つは、視力障害やその他の障害を持つ人々を含む、誰もが使用できることを保証することです。ReactおよびReact Native用のアクセシビリティAPIを使用すると、目が見えない人や視覚障害者が使用するスクリーンリーダーなどの支援技術を使用する人が、Reactを利用したあらゆるエクスペリエンスを使用できるようにすることができます。

この投稿では、React Nativeアプリに焦点を当てます。ReactアクセシビリティAPIは、AndroidおよびiOS APIと同様のルックアンドフィールになるように設計しました。以前にAndroid、iOS、またはWeb向けのアクセシブルアプリケーションを開発したことがある場合は、React AX APIのフレームワークと用語に慣れているはずです。たとえば、UI要素を*アクセシブル*(したがって、支援技術に公開)にすることができ、*accessibilityLabel*を使用して要素の文字列の説明を提供できます。

<View accessible={true} accessibilityLabel=”This is simple view”>

Facebook自身のReactを利用した製品である**広告マネージャーアプリ**を見て、React AX APIのもう少し複雑なアプリケーションについて見ていきましょう。

これは抜粋です。投稿の残りの部分は、Facebook Codeでお読みください。