「engineering」タグが付いた投稿 26 件
すべてのタグを表示React Native 0.71-RC0 Android停止の事後分析
0.71が利用可能になったので、2022年11月4日にReact NativeとExpo Androidビルドの最初の0.71リリース候補をリリースしている間に、すべてのReact NativeバージョンのAndroidビルドを壊したインシデントに関する重要な情報を共有したいと思います。
インシデントの対応に尽力してくれたコントリビューターは、最近、事後分析会議に出席し、何が起こったのか、そこから何を学んだのか、そして今後同様の停止を回避するためにどのような行動をとるのかを詳細に議論しました。
TypeScriptのファーストクラスサポート
0.71のリリースに伴い、React NativeはTypeScriptエクスペリエンスに以下の変更を加えて投資しています
この記事では、これらの変更がTypeScriptまたはFlowユーザーにとって何を意味するのかについて説明します。
iOS 15とAndroid 12向けにアプリを準備する
こんにちは皆さん!
今年後半に新しいモバイルOSバージョンがリリースされるため、リリースが一般提供される際にregressionを回避するために、事前にReact Nativeアプリを準備することをお勧めします。
新しいiOS WebViewsの紹介
Appleは長い間、WKWebViewを支持してUIWebViewsの使用を推奨していませんでした。今後数か月でリリースされるiOS 12では、UIWebViewsは正式に廃止されます。React NativeのiOS WebView実装は、UIWebViewクラスに大きく依存しています。したがって、これらの開発を踏まえて、WKWebViewを使用するWebView React Nativeコンポーネントに新しいネイティブiOSバックエンドを構築しました。
これらの変更の最終部分はこのコミットに含まれており、0.57リリースで利用可能になります。
この新しい実装を選択するには、useWebKit
プロパティを使用してください
<WebView
useWebKit={true}
source={{url: 'https://www.google.com'}}
/>
改善点
UIWebView
には、WebViewで実行されているJavaScriptとReact Native間の通信を円滑化するための正当な方法がありませんでした。WebViewからメッセージが送信された場合、それらをReact Nativeに配信するためにハックに依存していました。簡潔に言うと、メッセージデータを特別なスキームを持つURLにエンコードし、WebViewをナビゲートしました。ネイティブ側では、このナビゲーションを傍受してキャンセルし、URLからデータを解析し、最後にReact Nativeを呼び出しました。この実装はエラーが発生しやすく、安全ではありませんでした。それを完全に置き換えるためにWKWebView
機能を活用したことを発表できて嬉しく思います。
WKWebViewがUIWebViewよりも優れているその他の利点には、JavaScriptの実行が高速であること、およびマルチプロセスアーキテクチャがあります。詳細については、2014 WWDCをご覧ください。
注意事項
コンポーネントが以下のプロパティを使用している場合、WKWebViewに切り替えると問題が発生する可能性があります。当面は、これらのプロパティの使用を避けることをお勧めします
一貫性のない動作
automaticallyAdjustContentInsets
とcontentInsets
(コミット)
WKWebView
にcontentInsetsを追加しても、WKWebView
のビューポートは変更されません。ビューポートはフレームと同じサイズのままです。UIWebView
では、ビューポートのサイズが実際に変化します(コンテンツのインセットが正の場合、小さくなります)。
backgroundColor
(コミット)
WebViewの新しいiOS実装では、このプロパティを使用すると背景色がちらつく可能性があります。さらに、WKWebView
は透明な背景をUIWebview
とは異なる方法でレンダリングします。詳細については、コミットの説明をご覧ください。
サポートされていません
scalesPageToFit
(コミット)
WKWebViewはscalesPageToFitプロパティをサポートしていなかったため、WebView React Nativeコンポーネントにこれを実装できませんでした。
アクセシビリティAPIの更新
動機
テクノロジーが進歩し、モバイルアプリが日常生活でますます重要になるにつれて、アクセスしやすいアプリケーションを作成する必要性も同様に重要になっています。
React Nativeの限定的なアクセシビリティAPIは、常に開発者にとって大きな課題となっていました。そのため、アクセシビリティAPIにいくつかの更新を加え、インクルーシブなモバイルアプリケーションの作成を容易にしました。
既存のAPIの問題点
問題1:2つの全く異なるが類似したプロパティ - accessibilityComponentType(Android)と accessibilityTraits(iOS)
accessibilityComponentType
と accessibilityTraits
は、AndroidのTalkBackとiOSのVoiceOverに、ユーザーが操作しているUI要素の種類を伝えるために使用される2つのプロパティです。これらのプロパティに関する2つの最大の問題点は、
- **同じ目的を持ちながら、使用方法が異なる2つの異なるプロパティであること。** 以前のAPIでは、これらは(プラットフォームごとに1つずつ)2つの別々のプロパティであり、不便なだけでなく、多くの開発者にとって混乱を招くものでした。 iOSの
accessibilityTraits
は17種類の値を許可しますが、AndroidのaccessibilityComponentType
は4種類の値しか許可しません。さらに、ほとんどの場合、値に重複はありませんでした。これらの2つのプロパティの入力タイプも異なります。accessibilityTraits
は、特性の配列または単一の特性を渡すことができますが、accessibilityComponentType
は単一の値のみを許可します。 - **Androidの機能が非常に限られていること。** 古いプロパティでは、Talkbackが認識できるUI要素は、「button」、「radiobutton_checked」、および「radiobutton_unchecked」のみでした。
問題2:アクセシビリティヒントが存在しない:
アクセシビリティヒントは、TalkBackまたはVoiceOverを使用しているユーザーが、アクセシビリティラベルだけでは明らかではないアクセシビリティ要素に対してアクションを実行したときに何が起こるかを理解するのに役立ちます。これらのヒントは、設定パネルでオンとオフを切り替えることができます。以前は、React NativeのAPIはアクセシビリティヒントをまったくサポートしていませんでした。
問題3:反転色の無視:
視覚障碍のある一部のユーザーは、画面のコントラストを高めるために、携帯電話で反転色を使用しています。 Appleは、開発者が特定のビューを無視できるようにするAPIをiOS向けに提供しました。これにより、ユーザーが反転色の設定をオンにしている場合でも、画像やビデオが歪むことはありません。このAPIは現在、React Nativeではサポートされていません。
新しいAPIの設計
解決策1:accessibilityComponentType(Android)とaccessibilityTraits(iOS)の統合
accessibilityComponentType
とaccessibilityTraits
の混乱を解消するために、これらを単一のプロパティに統合することにしました。技術的には同じ機能を意図していたため、統合することで、開発者はアクセシビリティ機能を構築する際にプラットフォーム固有の複雑さを気にする必要がなくなりました。
背景
iOSでは、UIAccessibilityTraits
はNSObjectに設定できるプロパティです。 JavaScriptプロパティを介してネイティブに渡される17の特性はそれぞれ、Objective-CのUIAccessibilityTraits
要素にマッピングされます。特性はそれぞれlong intで表され、設定されているすべての特性はOR演算で結合されます。
しかし、Androidでは、AccessibilityComponentType
はReact Nativeによって作成された概念であり、Androidのプロパティに直接マッピングされるわけではありません。アクセシビリティは、アクセシビリティデリゲートによって処理されます。各ビューには、デフォルトのアクセシビリティデリゲートがあります。アクセシビリティアクションをカスタマイズする場合は、新しいアクセシビリティデリゲートを作成し、カスタマイズする特定のメソッドをオーバーライドしてから、処理しているビューのアクセシビリティデリゲートを新しいデリゲートに関連付けられるように設定する必要があります。開発者がAccessibilityComponentType
を設定すると、ネイティブコードは渡されたコンポーネントに基づいて新しいデリゲートを作成し、ビューにそのアクセシビリティデリゲートを設定しました。
変更点
新しいプロパティでは、2つのプロパティのスーパーセットを作成したかったのです。 accessibilityTraits
の方がはるかに多くの値を持っているため、新しいプロパティを既存のプロパティaccessibilityTraits
に基づいてモデル化することにしました。これらの特性のAndroidの機能は、アクセシビリティデリゲートを変更することでポリフィルされます。
iOSのaccessibilityTraits
に設定できるUIAccessibilityTraits
の値は17個あります。ただし、これらのすべてを新しいプロパティの可能な値として含めたわけではありません。これは、これらの特性の一部を設定することの効果があまりよく知られておらず、これらの値の多くが事実上使用されていないためです。
UIAccessibilityTraits
が設定された値は、一般的に2つの目的のいずれかを取りました。 UI要素の役割を説明するか、UI要素の状態を説明します。私たちが観察した以前のプロパティのほとんどの使用例では、通常、役割を表す1つの値を使用し、それを「状態:選択済み」、「状態:無効」、またはその両方と組み合わせていました。したがって、2つの新しいアクセシビリティプロパティを作成することにしました:accessibilityRole
とaccessibilityState
です。
accessibilityRole
新しいプロパティであるaccessibilityRole
は、TalkbackまたはVoiceoverにUI要素の役割を伝えるために使用されます。この新しいプロパティは、次のいずれかの値を取ることができます。
none
button
link
search
image
keyboardkey
text
adjustable
header
summary
imagebutton
UI要素は一般的にこれらの複数の役割を論理的に担うことはないため、このプロパティは1つの値のみを渡すことができます。例外はimageとbuttonなので、両方を組み合わせた役割imagebuttonを追加しました。
accessibilityStates
新しいプロパティであるaccessibilityStates
は、TalkbackまたはVoiceoverにUI要素の状態を伝えるために使用されます。このプロパティは、次の値の1つまたは両方を含む配列を取ります。
selected
disabled
解決策2:アクセシビリティヒントの追加
このため、新しいプロパティであるaccessibilityHint
を追加しました。このプロパティを設定すると、TalkbackまたはVoiceoverがユーザーにヒントを読み上げることができます。
accessibilityHint
このプロパティは、文字列形式で読み取られるアクセシビリティヒントを受け取ります。
iOSでは、このプロパティを設定すると、ビューに対応するネイティブプロパティAccessibilityHintが設定されます。 iPhoneでアクセシビリティヒントがオンになっている場合、ヒントはVoiceoverによって読み上げられます。
Androidでは、このプロパティを設定すると、ヒントの値がアクセシビリティラベルの末尾に追加されます。この実装の利点は、iOSのヒントの動作を模倣することですが、欠点は、これらのヒントをiOSのようにAndroidの設定でオフにできないことです。
Androidでこの決定を下した理由は、通常、アクセシビリティヒントは特定のアクション(クリックなど)に対応しており、プラットフォーム間で動作の一貫性を保ちたかったためです。
問題3の解決策
accessibilityIgnoresInvertColors
AppleのAPIであるAccessibilityIgnoresInvertColorsをJavaScriptに公開したので、色を反転させたくないビュー(画像など)がある場合は、このプロパティをtrueに設定すると、反転されません。
新しい使用方法
これらの新しいプロパティは、React Native 0.57リリースで利用可能になります。
アップグレード方法
現在accessibilityComponentType
とaccessibilityTraits
を使用している場合は、新しいプロパティにアップグレードするために次の手順を実行できます。
1. jscodeshiftの使用
最も単純なユースケースは、jscodeshiftスクリプトを実行することで置き換えることができます。
このスクリプトは、次のインスタンスを置き換えます
accessibilityTraits=“trait”
accessibilityTraits={[“trait”]}
と
accessibilityRole= “trait”
このスクリプトは、AccessibilityComponentType
のインスタンスも削除します(AccessibilityComponentType
を設定するすべての場所でAccessibilityTraits
も設定すると仮定)。
2. 手動コードモッドの使用
AccessibilityRole
に対応する値を持たないAccessibilityTraits
を使用した場合、および複数の特性がAccessibilityTraits
に渡された場合は、手動コードモッドを実行する必要があります。
一般的に、
accessibilityTraits= {[“button”, “selected”]}
は、手動で
accessibilityRole=“button”
accessibilityStates={[“selected”]}
に置き換えられます。これらのプロパティは、すでにFacebookのコードベースで使用されています。 Facebookのコードモッドは驚くほど簡単でした。 jscodeshiftスクリプトは約半分のインスタンスを修正し、残りの半分は手動で修正されました。全体として、プロセス全体は数時間未満で完了しました。
更新されたAPIがお役に立てば幸いです!そして、アプリのアクセシビリティを高め続けてください!#inclusion
React Nativeの現状 2018
React Nativeに関するステータスアップデートを最後に公開してからしばらく経ちました。
Facebookでは、これまで以上に多くの重要なプロジェクトでReact Nativeを使用しています。最も人気のある製品の1つはMarketplaceであり、アプリのトップレベルタブの1つであり、毎月8億人が使用しています。 2015年の作成以来、Marketplace全体はReact Nativeで構築されており、アプリのさまざまな部分に100を超えるフルスクリーンビューがあります。
また、アプリの多くの新しい部分にもReact Nativeを使用しています。先月のF8基調講演をご覧になった方は、献血、危機対応、プライバシーショートカット、健康チェックなど、React Nativeで構築された最近の機能をご存知でしょう。また、メインのFacebookアプリ以外のプロジェクトでもReact Nativeが使用されています。新しいOculus Go VRヘッドセットには、React Nativeで完全に構築されたコンパニオンモバイルアプリが含まれています。ヘッドセット自体で多くのエクスペリエンスを強化するReact VRは言うまでもありません。
当然のことながら、アプリの構築には他の多くのテクノロジーも使用しています。 LithoとComponentKitは、アプリで広く使用している2つのライブラリです。どちらも、ネイティブ画面を構築するためのReactのようなコンポーネントAPIを提供します。 React Nativeが他のすべてのテクノロジーに取って代わることは決して目標ではありませんでした。私たちはReact Native自体を改善することに焦点を当てていますが、他のチームがReact Nativeからアイデアを借用するのを見るのは大好きです。インスタントリロードをJavaScript以外のコードにも導入するなどです。
アーキテクチャ
2013年にReact Nativeプロジェクトを開始した時、JavaScriptとネイティブ間の「ブリッジ」を非同期、シリアライズ可能、そしてバッチ処理されるように設計しました。React DOMがReactの状態更新を`document.createElement(attrs)`や`.appendChild()`のようなDOM APIへの命令的で変更的な呼び出しに変換するのと同じように、React Nativeは`[["createView", attrs], ["manageChildren", ...]]`のように実行する変更をリスト化した単一のJSONメッセージを返すように設計されました。私たちは、同期応答の返却に依存せず、そのリスト内のすべてがJSONに完全にシリアライズされ、元に戻せることを保証するために、システム全体を設計しました。これは、私たちに柔軟性をもたらすためでした。このアーキテクチャの上に、WebSocket接続を介してすべてのJavaScriptコードを非同期で実行するChromeデバッグなどのツールを構築することができました。
過去5年間で、これらの初期原則がいくつかの機能の構築を困難にしていることが分かりました。非同期ブリッジは、同期応答を期待する多くのネイティブAPIとJavaScriptロジックを直接統合できないことを意味します。ネイティブ呼び出しをキューに入れるバッチ処理ブリッジは、React Nativeアプリがネイティブに実装されている関数を呼び出すことを難しくします。そして、シリアライズ可能なブリッジは、2つの世界間でメモリを直接共有する代わりに、不必要なコピーを意味します。完全にReact Nativeで構築されたアプリの場合、これらの制限は通常許容範囲内です。しかし、React Nativeと既存のアプリコード間の複雑な統合を持つアプリにとっては、イライラするものです。
React Nativeの大規模な再アーキテクチャに取り組んでおり、フレームワークをより柔軟にし、ハイブリッドJavaScript/ネイティブアプリのネイティブインフラストラクチャとの統合を改善します。このプロジェクトでは、過去5年間で学んだことを適用し、アーキテクチャをより現代的なものへと段階的に移行していきます。React Nativeの内部の多くを書き直していますが、変更のほとんどは内部的なものです。既存のReact Nativeアプリは、変更なし、またはわずかな変更で引き続き動作します。
React Nativeをより軽量化し、既存のネイティブアプリにうまく適合させるために、この再アーキテクチャには3つの主要な内部変更があります。まず、スレッディングモデルを変更しています。各UI更新で3つの異なるスレッドで作業を実行する必要がある代わりに、応答性を維持するために優先度の低い作業をメインスレッドから外しながら、優先度の高い更新のために任意のスレッドでJavaScriptを同期的に呼び出すことができるようになります。次に、非同期レンダリング機能をReact Nativeに組み込み、複数のレンダリング優先順位を許可し、非同期データ処理を簡素化します。最後に、ブリッジを簡素化して、より高速で軽量にします。ネイティブとJavaScript間の直接呼び出しはより効率的で、言語をまたがるスタックトレースなどのデバッグツールの構築を容易にします。
これらの変更が完了すると、より緊密な統合が可能になります。現在、複雑なハックなしに、ネイティブナビゲーションとジェスチャ処理、またはUICollectionViewやRecyclerViewなどのネイティブコンポーネントを組み込むことはできません。スレッディングモデルを変更した後、このような機能の構築は簡単になります。
今年後半、完成が近づくにつれて、この作業の詳細をさらに発表する予定です。
コミュニティ
Facebook内のコミュニティに加えて、Facebook外にもReact Nativeユーザーと協力者の活発なコミュニティがあることを嬉しく思っています。React Nativeユーザーへのサービス向上とプロジェクトへの貢献を容易にすることで、React Nativeコミュニティをさらにサポートしたいと考えています。
アーキテクチャの変更がReact Nativeと他のネイティブインフラストラクチャとのよりクリーンな相互運用に役立つように、React NativeはJavaScript側でスリム化され、VMとバンドラーを交換可能にするなど、JavaScriptエコシステムにうまく適合する必要があります。破壊的な変更のペースは追いつくのが難しい場合があるため、メジャーリリースの回数を減らす方法を見つけたいと考えています。最後に、スタートアップの最適化など、私たちの専門知識がまだ文書化されていない分野で、より詳細なドキュメントを求めているチームがあることを認識しています。来年には、これらの変更の一部が見られると期待してください。
React Nativeを使用している場合、あなたは私たちのコミュニティの一員です。React Nativeをより良くするために私たちにできることを教えてください。
React Nativeは、モバイル開発者のツールボックスの中の1つのツールに過ぎませんが、私たちが強く信じているツールです。そして、昨年、500人以上の貢献者から2500以上のコミットがあり、日々改善しています。
React NativeでTypeScriptを使用する
JavaScript! みんな大好きです。しかし、中には型が好きな人もいます。幸いなことに、JavaScriptにより強い型を追加するオプションがあります。私のお気に入りはTypeScriptですが、React NativeはFlowをそのままサポートしています。どちらを好むかは好みの問題であり、それぞれJavaScriptに型の魔法を追加する方法について独自のアプローチを持っています。今日は、React NativeアプリでTypeScriptを使用する方法を見ていきます。
この記事では、MicrosoftのTypeScript-React-Native-Starterリポジトリをガイドとして使用しています。
更新: このブログ記事が書かれてから、さらに簡単になりました. このブログ記事で説明されているすべての設定は、たった1つのコマンドを実行することで置き換えることができます
npx react-native init MyAwesomeProject --template react-native-template-typescript
ただし、BabelのTypeScriptサポートにはいくつかの制限があり、上記のブログ記事で詳しく説明しています。*この記事*で説明されている手順はまだ機能しており、Artsyは本番環境でreact-native-typescript-transformerを使用していますが、React NativeとTypeScriptで起動して実行する最も速い方法は、上記のコマンドを使用することです。必要に応じて、後でいつでも切り替えることができます。
とにかく、楽しんでください!元のブログ記事は以下に続きます。
前提条件
複数の異なるプラットフォームで開発し、複数の異なるタイプのデバイスをターゲットにする可能性があるため、基本的なセットアップが必要になる場合があります。最初に、TypeScriptなしでプレーンなReact Nativeアプリを実行できることを確認する必要があります。React NativeのWebサイトにある手順に従って開始してください。デバイスまたはエミュレータにデプロイできたら、TypeScript React Nativeアプリを開始する準備が整います.
初期化
通常のReact Nativeプロジェクトのスキャフォールディングを試したら、TypeScriptの追加を開始する準備が整います。さあ、それをやってみましょう。
react-native init MyAwesomeProject
cd MyAwesomeProject
TypeScriptの追加
次の手順は、プロジェクトにTypeScriptを追加することです。以下のコマンドは
- プロジェクトにTypeScriptを追加します
- プロジェクトにReact Native TypeScript Transformerを追加します
- 次に設定する空のTypeScript設定ファイルを初期化します
- 次に設定する空のReact Native TypeScript Transformer設定ファイルを追加します
- ReactとReact Nativeの型定義を追加します
さて、これらを実行してみましょう。
yarn add --dev typescript
yarn add --dev react-native-typescript-transformer
yarn tsc --init --pretty --jsx react
touch rn-cli.config.js
yarn add --dev @types/react @types/react-native
`tsconfig.json`ファイルには、TypeScriptコンパイラのすべての設定が含まれています。上記のコマンドによって作成されたデフォルトはほとんど問題ありませんが、ファイルを開いて次の行のコメントを外します
{
/* Search the config file for the following line and uncomment it. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
}
`rn-cli.config.js`には、React Native TypeScript Transformerの設定が含まれています。開いて、以下を追加します
module.exports = {
getTransformModulePath() {
return require.resolve('react-native-typescript-transformer');
},
getSourceExts() {
return ['ts', 'tsx'];
},
};
TypeScriptへの移行
生成された`App.js`および`__tests_/App.js`ファイルを`App.tsx`に名前変更します。`index.js`は`.js`拡張子を使用する必要があります。すべての新しいファイルは`.tsx`拡張子(JSXが含まれていない場合は`.ts`)を使用する必要があります。
ここでアプリを実行しようとすると、「オブジェクトプロトタイプはオブジェクトまたはnullのみ可能です」のようなエラーが発生します。これは、Reactからのデフォルトエクスポートと、同じ行の名前付きエクスポートのインポートに失敗したことが原因です。`App.tsx`を開き、ファイルの先頭にあるインポートを変更します
-import React, { Component } from 'react';
+import React from 'react'
+import { Component } from 'react';
これの一部は、BabelとTypeScriptがCommonJSモジュールと相互運用する方法の違いに関係しています。将来的には、2つは同じ動作で安定します。
この時点で、React Nativeアプリを実行できるはずです。
TypeScriptテストインフラストラクチャの追加
React NativeにはJestが付属しているため、TypeScriptでReact Nativeアプリをテストするためには、`devDependencies`にts-jestを追加する必要があります。
yarn add --dev ts-jest
次に、`package.json`を開き、`jest`フィールドを次のように置き換えます
{
"jest": {
"preset": "react-native",
"moduleFileExtensions": [
"ts",
"tsx",
"js"
],
"transform": {
"^.+\\.(js)$": "<rootDir>/node_modules/babel-jest",
"\\.(ts|tsx)$": "<rootDir>/node_modules/ts-jest/preprocessor.js"
},
"testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$",
"testPathIgnorePatterns": [
"\\.snap$",
"<rootDir>/node_modules/"
],
"cacheDirectory": ".jest/cache"
}
}
これにより、Jestは`.ts`および`.tsx`ファイルを`ts-jest`で実行するように設定されます。
依存関係型宣言のインストール
TypeScriptで最高のエクスペリエンスを得るには、型チェッカーが依存関係の形状とAPIを理解するようにする必要があります。一部のライブラリは、`.d.ts`ファイル(型宣言/型定義ファイル)を含むパッケージを公開します。これは、基礎となるJavaScriptの形状を記述できます。他のライブラリの場合、`@types/` npmスコープで適切なパッケージを明示的にインストールする必要があります。
たとえば、ここではJest、React、React Native、およびReact Test Rendererの型が必要です。
yarn add --dev @types/jest @types/react @types/react-native @types/react-test-renderer
これらの宣言ファイルパッケージは、開発時にのみこれらの依存関係を使用し、実行時には使用しないReact Native *アプリ*であるため、*dev* 依存関係として保存しました。ライブラリをNPMに公開する場合、これらの型依存関係の一部を通常の依存関係として追加する必要がある場合があります。
.d.ts
ファイルの取得方法の詳細については、こちらをご覧ください。
より多くのファイルの無視
ソース管理では、.jest
フォルダを無視し始める必要があります。 gitを使用している場合は、.gitignore
ファイルにエントリを追加するだけです。
# Jest
#
.jest/
チェックポイントとして、ファイルをバージョン管理にコミットすることを検討してください。
git init
git add .gitignore # import to do this first, to ignore our files
git add .
git commit -am "Initial commit."
コンポーネントの追加
アプリにコンポーネントを追加してみましょう。 Hello.tsx
コンポーネントを作成しましょう。これは、実際にアプリで記述するものではなく、React NativeでTypeScriptを使用する方法を示す、教育的なコンポーネントです。
components
ディレクトリを作成し、次の例を追加します。
// components/Hello.tsx
import React from 'react';
import {Button, StyleSheet, Text, View} from 'react-native';
export interface Props {
name: string;
enthusiasmLevel?: number;
}
interface State {
enthusiasmLevel: number;
}
export class Hello extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
if ((props.enthusiasmLevel || 0) <= 0) {
throw new Error(
'You could be a little more enthusiastic. :D',
);
}
this.state = {
enthusiasmLevel: props.enthusiasmLevel || 1,
};
}
onIncrement = () =>
this.setState({
enthusiasmLevel: this.state.enthusiasmLevel + 1,
});
onDecrement = () =>
this.setState({
enthusiasmLevel: this.state.enthusiasmLevel - 1,
});
getExclamationMarks = (numChars: number) =>
Array(numChars + 1).join('!');
render() {
return (
<View style={styles.root}>
<Text style={styles.greeting}>
Hello{' '}
{this.props.name +
this.getExclamationMarks(this.state.enthusiasmLevel)}
</Text>
<View style={styles.buttons}>
<View style={styles.button}>
<Button
title="-"
onPress={this.onDecrement}
accessibilityLabel="decrement"
color="red"
/>
</View>
<View style={styles.button}>
<Button
title="+"
onPress={this.onIncrement}
accessibilityLabel="increment"
color="blue"
/>
</View>
</View>
</View>
);
}
}
// styles
const styles = StyleSheet.create({
root: {
alignItems: 'center',
alignSelf: 'center',
},
buttons: {
flexDirection: 'row',
minHeight: 70,
alignItems: 'stretch',
alignSelf: 'center',
borderWidth: 5,
},
button: {
flex: 1,
paddingVertical: 0,
},
greeting: {
color: '#999',
fontWeight: 'bold',
},
});
うわー!たくさんありますが、分解してみましょう
div
、span
、h1
などのHTML要素をレンダリングする代わりに、View
やButton
などのコンポーネントをレンダリングしています。これらは、異なるプラットフォームで動作するネイティブコンポーネントです。- スタイリングは、React Nativeが提供する
StyleSheet.create
関数を使用して指定されます。 Reactのスタイルシートを使用すると、Flexboxを使用してレイアウトを制御し、CSSと同様の他の構造を使用してスタイルを設定できます。
コンポーネントテストの追加
コンポーネントができたので、テストしてみましょう。
テストランナーとしてJestがすでにインストールされています。コンポーネントのスナップショットテストを作成します。スナップショットテストに必要なアドオンを追加しましょう
yarn add --dev react-addons-test-utils
次に、components
ディレクトリに__tests__
フォルダーを作成し、Hello.tsx
のテストを追加します
// components/__tests__/Hello.tsx
import React from 'react';
import renderer from 'react-test-renderer';
import {Hello} from '../Hello';
it('renders correctly with defaults', () => {
const button = renderer
.create(<Hello name="World" enthusiasmLevel={1} />)
.toJSON();
expect(button).toMatchSnapshot();
});
テストが初めて実行されると、レンダリングされたコンポーネントのスナップショットが作成され、components/__tests__/__snapshots__/Hello.tsx.snap
ファイルに保存されます。コンポーネントを変更するときは、スナップショットを更新し、意図しない変更がないか更新を確認する必要があります。 React Nativeコンポーネントのテストの詳細については、こちらをご覧ください。
次のステップ
公式のReactチュートリアルと状態管理ライブラリReduxをご覧ください。これらのリソースは、React Nativeアプリを作成するときに役立ちます。さらに、Web上のReactとReact Nativeの両方をサポートする、TypeScriptで完全に記述されたコンポーネントライブラリであるReactXPもご覧ください。
よりタイプセーフなReact Native開発環境をお楽しみください!
React Native向けに<InputAccessoryView>を構築する
動機
3年前、React Nativeから入力アクセサリビューをサポートするためのGitHub issueが開かれました。

その後、数え切れないほどの「+1」、さまざまな回避策、そしてこの問題に関するRNへの具体的な変更は今日までありませんでした。 iOSから始めて、ネイティブ入力アクセサリビューにアクセスするためのAPIを公開しました。どのように構築したかをご紹介できることを嬉しく思います。
背景
入力アクセサリビューとは正確には何でしょうか? Appleの開発者向けドキュメントを読むと、レシーバーがファーストレスポンダーになったときにシステムキーボードの上部に固定できるカスタムビューであることがわかります。 UIResponder
から継承するものはすべて、.inputAccessoryView
プロパティを読み書きとして再宣言し、ここでカスタムビューを管理できます。レスポンダーインフラストラクチャはビューをマウントし、システムキーボードと同期させています。ドラッグやタップなど、キーボードを閉じるジェスチャは、フレームワークレベルで入力アクセサリビューに適用されます。これにより、インタラクティブなキーボードのdismissalを備えたコンテンツを構築できます。これは、iMessageやWhatsAppなどのトップクラスのメッセージングアプリに不可欠な機能です。
キーボードの上部にビューを固定するには、2つの一般的なユースケースがあります。 1つ目は、Facebookのcomposer background pickerのようなキーボードツールバーを作成することです。

このシナリオでは、キーボードはテキスト入力フィールドに焦点を合わせており、入力アクセサリビューは追加のキーボード機能を提供するために使用されます。この機能は、入力フィールドのタイプにコンテキスト依存します。マッピングアプリケーションでは住所の候補、テキストエディターではリッチテキストの書式設定ツールなどです。
このシナリオで<InputAccessoryView>
を所有するObjective-C UIResponderは明確である必要があります。 <TextInput>
がファーストレスポンダーになり、内部的にはUITextView
またはUITextField
のインスタンスになります。
2番目の一般的なシナリオは、スティッキーテキスト入力です

ここでは、テキスト入力は実際に入力アクセサリビューの一部です。これは、以前のメッセージのスレッドをスクロールしながらメッセージを作成できるメッセージングアプリケーションでよく使用されます。
この例では、誰が<InputAccessoryView>
を所有しているのでしょうか? 再びUITextView
またはUITextField
になるのでしょうか? テキスト入力は入力アクセサリビューの*内部*にあります。これは循環依存のように聞こえます。この問題を解決すること自体が、別のブログ記事です。ネタバレ:所有者は、手動でbecomeFirstResponderするように指示する汎用UIView
サブクラスです。
API設計
これで、<InputAccessoryView>
とは何か、そしてどのように使用したいかがわかりました。次のステップは、両方のユースケースで意味があり、<TextInput>
などの既存のReact Nativeコンポーネントとうまく連携するAPIを設計することです。
キーボードツールバーの場合、考慮すべき点がいくつかあります
- 汎用React Nativeビュー階層を
<InputAccessoryView>
に巻き上げることができるようにしたい。 - この汎用でデタッチされたビュー階層がタッチを受け入れ、アプリケーションの状態を操作できるようにしたい。
<InputAccessoryView>
を特定の<TextInput>
にリンクしたい。- コードを複製することなく、複数のテキスト入力で
<InputAccessoryView>
を共有できるようにしたい。
Reactポータルと同様の概念を使用して、#1を実現できます。この設計では、React Nativeビューを、レスポンダーインフラストラクチャによって管理されるUIView
階層にポータルします。 React NativeビューはUIViewとしてレンダリングされるため、これは実際には非常に簡単です。オーバーライドするだけです
- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex
すべてのサブビューを新しいUIView階層にパイプします。 #2の場合、<InputAccessoryView>
に新しいRCTTouchHandlerを設定します。状態の更新は、通常のイベントコールバックを使用して実現されます。 #3と#4の場合、nativeIDフィールドを使用して、<TextInput>
コンポーネントの作成中にネイティブコードでアクセサリビューUIView階層を見つけます。この関数は、基になるネイティブテキスト入力の.inputAccessoryView
プロパティを使用します。これを行うと、ObjC実装で<InputAccessoryView>
が<TextInput>
に効果的にリンクされます。
スティッキーテキスト入力(シナリオ2)をサポートすると、さらにいくつかの制約が追加されます。この設計では、入力アクセサリビューにテキスト入力が子として含まれているため、nativeIDを介したリンクはオプションではありません。代わりに、汎用オフスクリーンUIView
の.inputAccessoryView
をネイティブ<InputAccessoryView>
階層に設定します。この汎用UIView
に手動でファーストレスポンダーになるように指示することにより、階層はレスポンダーインフラストラクチャによってマウントされます。この概念は、前述のブログ記事で詳しく説明されています。
落とし穴
もちろん、このAPIの構築中にすべてが順風満帆だったわけではありません。発生した落とし穴と、それらを修正した方法を次に示します。
このAPIを構築するための最初のアイデアは、UIKeyboardWill(Show / Hide / ChangeFrame)イベントのNSNotificationCenter
をリッスンすることでした。このパターンは、一部のオープンソースライブラリで使用されており、Facebookアプリの内部でも使用されています。残念ながら、UIKeyboardDidChangeFrame
イベントは、スワイプで<InputAccessoryView>
フレームを更新するのに間に合うように呼び出されていませんでした。また、キーボードの高さの変更は、これらのイベントではキャプチャされません。これは、次のように現れるバグのクラスを作成します

iPhone Xでは、テキストキーボードと絵文字キーボードの高さは異なります。キーボードイベントを使用してテキスト入力フレームを操作するほとんどのアプリケーションは、上記のバグを修正する必要がありました。私たちのソリューションは、.inputAccessoryView
プロパティを使用することにコミットすることでした。これは、レスポンダーインフラストラクチャがこのようなフレームの更新を処理することを意味します。
遭遇したもう1つの厄介なバグは、iPhone Xのホームピルを避けることでした。「AppleはこのまさにそのためにsafeAreaLayoutGuideを開発しました。これは些細なことです!」と思っているかもしれません。私たちも同様にナイーブでした。最初の問題は、ネイティブの<InputAccessoryView>
実装には、表示される直前までアンカーするウィンドウがないことです。大丈夫です。-(BOOL)becomeFirstResponder
をオーバーライドして、そこでレイアウト制約を適用できます。これらの制約に従うとアクセサリビューが上に移動しますが、別のバグが発生します:
入力アクセサリビューはホームピルをうまく回避しますが、安全でない領域の背後にあるコンテンツが表示されるようになりました。解決策はこのレーダーにあります。ネイティブの<InputAccessoryView>
階層を、safeAreaLayoutGuide
制約に準拠していないコンテナーでラップしました。ネイティブコンテナーは安全でない領域のコンテンツをカバーしますが、<InputAccessoryView>
は安全な領域の境界内に留まります。
使用例
<TextInput>
状態をリセットするためのキーボードツールバーボタンを作成する例を次に示します。
class TextInputAccessoryViewExample extends React.Component<
{},
*,
> {
constructor(props) {
super(props);
this.state = {text: 'Placeholder Text'};
}
render() {
const inputAccessoryViewID = 'inputAccessoryView1';
return (
<View>
<TextInput
style={styles.default}
inputAccessoryViewID={inputAccessoryViewID}
onChangeText={text => this.setState({text})}
value={this.state.text}
/>
<InputAccessoryView nativeID={inputAccessoryViewID}>
<View style={{backgroundColor: 'white'}}>
<Button
onPress={() =>
this.setState({text: 'Placeholder Text'})
}
title="Reset Text"
/>
</View>
</InputAccessoryView>
</View>
);
}
}
スティッキーテキスト入力の別の例はリポジトリにあります。
いつから使用できますか?
この機能実装の完全なコミットはこちらです。 <InputAccessoryView>
は、近日リリース予定のv0.55.0で使用可能になります。
快適なキーボード操作を :)
React NativeでAWSを使用する
AWSは、コンピューティング、ストレージ、データベース技術、そしてフルマネージドのサーバーレスサービスなど、クラウドサービスのプロバイダーとして、テクノロジー業界で広く知られています。 AWS Mobileチームは、お客様とJavaScriptエコシステムのメンバーと緊密に協力して、クラウド接続されたモバイルおよびウェブアプリケーションをより安全でスケーラブル、そして開発とデプロイが容易なものにするために取り組んできました。私たちは完全なスターターキットから始めましたが、最近ではさらにいくつかの開発を進めています。
このブログ記事では、ReactおよびReact Native開発者にとって興味深い内容について説明します。
- クラウドサービスを使用するJavaScriptアプリケーションのための宣言型ライブラリであるAWS Amplify
- オフラインおよびリアルタイム機能を備えたフルマネージドのGraphQLサービスであるAWS AppSync
AWS Amplify
React Nativeアプリケーションは、Create React Native AppやExpoのようなツールを使用することで、非常に簡単にブートストラップできます。しかし、ユースケースをインフラストラクチャサービスに適合させようとすると、クラウドへの接続は困難になる可能性があります。たとえば、React Nativeアプリで写真をアップロードする必要があるとします。これらはユーザーごとに保護する必要がありますか?おそらく、何らかの登録またはサインインプロセスが必要になるでしょう。独自のユーザーディレクトリを使用しますか、それともソーシャルメディアプロバイダーを使用しますか?ユーザーがログインした後、アプリがカスタムビジネスロジックを使用してAPIを呼び出す必要がある場合もあります。
JavaScript開発者がこれらの問題に対処できるように、AWS Amplifyというライブラリをリリースしました。この設計は、AWS固有の実装ではなく、タスクの「カテゴリ」に分類されています。たとえば、ユーザーの登録、ログイン、プライベート写真のアップロードを希望する場合は、アプリケーションにAuth
とStorage
のカテゴリを取り込むだけです。
import { Auth } from 'aws-amplify';
Auth.signIn(username, password)
.then(user => console.log(user))
.catch(err => console.log(err));
Auth.confirmSignIn(user, code)
.then(data => console.log(data))
.catch(err => console.log(err));
上記のコードでは、Amplifyが支援する一般的なタスクの例をいくつか示しています。たとえば、メールまたはSMSで多要素認証(MFA)コードを使用することなどです。現在サポートされているカテゴリは次のとおりです。
- Auth:認証の自動化を提供します。すぐに使える実装では、署名にAWS認証情報を使用し、Amazon CognitoからOIDC JWTトークンを使用します。MFA機能などの一般的な機能がサポートされています。
- Analytics:1行のコードで、Amazon Pinpointで認証済みまたは未認証ユーザーの追跡を取得します。必要に応じて、カスタムメトリクスまたは属性に拡張します。
- API:AWS署名バージョン4を活用して、RESTful APIとの安全なインタラクションを提供します。APIモジュールは、Amazon API Gatewayを使用したサーバーレスインフラストラクチャで優れています。
- Storage:Amazon S3でコンテンツをアップロード、ダウンロード、および一覧表示するための簡略化されたコマンド。また、ユーザーごとにデータをパブリックまたはプライベートコンテンツに簡単にグループ化することもできます。
- Caching:実装固有の永続性を使用する、ウェブアプリとReact NativeにわたるLRUキャッシュインターフェース。
- i18n and Logging:国際化とローカライズ機能、およびデバッグとロギング機能を提供します。
Amplifyの優れた点の1つは、特定のプログラミング環境の「ベストプラクティス」を設計に組み込んでいることです。たとえば、お客様やReact Native開発者と協力して発見したことの1つは、開発中に物事を迅速に進めるために行われた近道が、本番スタックにまで及んでしまうことです。これらはスケーラビリティまたはセキュリティを損ない、インフラストラクチャの再構築とコードのリファクタリングを強制する可能性があります。
開発者がこれを回避できるようにするための1つの例は、AWS Lambdaを使用したサーバーレスリファレンスアーキテクチャです。これらは、バックエンドを構築する際にAmazon API GatewayとAWS Lambdaを一緒に使用する際のベストプラクティスを示しています。このパターンは、AmplifyのAPI
カテゴリにエンコードされています。このパターンを使用して、いくつかの異なるRESTエンドポイントと対話し、カスタムビジネスロジックのためにLambda関数にヘッダーをすべて渡すことができます。また、これらの機能を使用して新規または既存のReact NativeプロジェクトをブートストラップするためのAWS Mobile CLIもリリースしました。開始するには、**npm**でインストールし、構成プロンプトに従ってください。
npm install --global awsmobile-cli
awsmobile configure
モバイルエコシステムに固有のエンコードされたベストプラクティスのもう1つの例は、パスワードセキュリティです。デフォルトのAuth
カテゴリ実装では、ユーザー登録とサインインにAmazon Cognitoユーザー プールを活用しています。このサービスは、認証の試行中にユーザーを保護する方法として、セキュアリモートパスワードプロトコルを実装しています。プロトコルの数学を読みたい場合は、プリミティブな根に対するパスワード検証者を計算してグループを生成する際に、大きな素数を使用する必要があることに気づかれるでしょう。React Native環境では、JITは無効になっています。これにより、このようなセキュリティ操作のためのBigInteger計算のパフォーマンスが低下します。これを考慮して、プロジェクト内にリンクできるAndroidとiOSのネイティブブリッジをリリースしました。
npm install --save aws-amplify-react-native
react-native link amazon-cognito-identity-js
Expoチームが最新のSDKにこれを含めたことを嬉しく思います。これにより、イジェクトすることなくAmplifyを使用できます。
最後に、React Native(およびReact)開発に固有のこととして、Amplifyには、アプリへのサインアップやサインインなどの機能を簡単にラップするための高階コンポーネント(HOC)が含まれています。
import Amplify, { withAuthenticator } from 'aws-amplify-react-native';
import aws_exports from './aws-exports';
Amplify.configure(aws_exports);
class App extends React.Component {
...
}
export default withAuthenticator(App);
基盤となるコンポーネントは<Authenticator />
としても提供されており、UIを完全にカスタマイズできます。また、ユーザーがサインインしたか、MFAの確認を待っているかなど、ユーザーの状態を管理するためのプロパティと、状態が変化したときに起動できるコールバックも提供されます。
同様に、さまざまなユースケースに使用できる一般的なReactコンポーネントがあります。たとえば、Storage
モジュールでAmazon S3のすべてのプライベート画像を表示するために、ニーズに合わせてカスタマイズできます。
<S3Album
level="private"
path={path}
filter={(item) => /jpg/i.test(item.path)}/>
前述のように、パブリックまたはプライベートストレージオプションを使用して、propsを介して多くのコンポーネント機能を制御できます。ユーザーが特定のUIコンポーネントと対話するときに自動的に分析を収集する機能さえあります。
return <S3Album track/>
AWS Amplifyは、グローバル初期化ルーチンまたはカテゴリレベルでの初期化により、設定よりも規約を重視した開発スタイルを推奨しています。aws-exportsファイルを使用するのが最も簡単な方法です。ただし、開発者は既存のリソースでライブラリを個別に使用することもできます。
哲学の詳細と完全なデモについては、AWS re:Inventのビデオをご覧ください。
AWS AppSync
AWS Amplifyの立ち上げ直後、AWS AppSyncもリリースしました。これは、オフラインとリアルタイムの両方の機能を備えたフルマネージドのGraphQLサービスです。GraphQLは、ネイティブAndroidやiOSを含むさまざまなクライアントプログラミング言語で使用できますが、React Native開発者の間で非常に人気があります。これは、データモデルが単方向データフローとコンポーネント階層によく適合するためです。
AWS AppSyncを使用すると、自分のAWSアカウントのリソースに接続できます。つまり、自分のデータを所有および制御できます。これはデータソースを使用して行われ、このサービスはAmazon DynamoDB、Amazon Elasticsearch、およびAWS Lambdaをサポートしています。これにより、NoSQLや全文検索などの機能をスキーマとして単一のGraphQL APIに結合できます。これにより、データソースを組み合わせることができます。AppSyncサービスは、スキーマからプロビジョニングすることもできるため、AWSサービスに慣れていない場合は、GraphQL SDLを記述してボタンをクリックするだけで、自動的に起動して実行できます。
AWS AppSyncのリアルタイム機能は、よく知られているイベントベースのパターンを使用したGraphQLサブスクリプションによって制御されます。AWS AppSyncのサブスクリプションは、GraphQLディレクティブを使用してスキーマで制御され、スキーマは任意のデータソースを使用できるため、Amazon DynamoDBおよびAmazon Elasticsearch Serviceを使用したデータベース操作、またはAWS Lambdaを使用したインフラストラクチャの他の部分から通知をトリガーできます。
AWS Amplifyと同様の方法で、エンタープライズセキュリティ機能をAWS AppSyncでGraphQL APIに使用できます。このサービスでは、APIキーを使用してすぐに開始できます。ただし、本番環境に移行する際には、AWS Identity and Access Management(IAM)またはAmazon Cognitoユーザー プールからのOIDCトークンを使用するように移行できます。タイプに対するポリシーを使用して、リゾルバーレベルでアクセスを制御できます。きめ細かいアクセス制御チェックを実行時に使用して、ユーザーが特定のデータベースリソースの所有者かどうかを検出するなどの論理チェックを実行することもできます。リゾルバーの実行または個々のデータベースレコードアクセスのためのグループメンバーシップをチェックする機能もあります。
React Native開発者がこれらのテクノロジーについてさらに学習できるように、AWS AppSyncコンソールホームページで起動できる組み込みのGraphQLサンプルスキーマがあります。このサンプルは、GraphQLスキーマをデプロイし、データベーステーブルをプロビジョニングし、クエリ、ミューテーション、およびサブスクリプションを自動的に接続します。この組み込みスキーマを活用するAWS AppSync用のReact Nativeの例(およびReactの例)も機能しており、クライアントとクラウドの両方のコンポーネントを数分で実行できます。
Apollo Clientにプラグインする`AWSAppSyncClient`を使用すると、簡単に開始できます。`AWSAppSyncClient`は、GraphQL APIのセキュリティと署名、オフライン機能、およびサブスクリプションのハンドシェイクとネゴシエーションプロセスを処理します。
import AWSAppSyncClient from "aws-appsync";
import { Rehydrated } from 'aws-appsync-react';
import { AUTH_TYPE } from "aws-appsync/lib/link/auth-link";
const client = new AWSAppSyncClient({
url: awsconfig.graphqlEndpoint,
region: awsconfig.region,
auth: {type: AUTH_TYPE.API_KEY, apiKey: awsconfig.apiKey}
});
AppSyncコンソールは、GraphQLエンドポイント、AWSリージョン、およびAPIキーを含む設定ファイルをダウンロード用に提供します。その後、React Apolloでクライアントを使用できます。
const WithProvider = () => (
<ApolloProvider client={client}>
<Rehydrated>
<App />
</Rehydrated>
</ApolloProvider>
);
この時点で、標準のGraphQLクエリを使用できます。
query ListEvents {
listEvents{
items{
__typename
id
name
where
when
description
comments{
__typename
items{
__typename
eventId
commentId
content
createdAt
}
nextToken
}
}
}
}
上記の例は、AppSyncによってプロビジョニングされたサンプルアプリスキーマを使用したクエリを示しています。DynamoDBとのインタラクションを紹介するだけでなく、データのページネーション(暗号化されたトークンを含む)と`Events`と`Comments`間の型の関係も含まれています。アプリは`AWSAppSyncClient`で構成されているため、データは自動的にオフラインで永続化され、デバイスが再接続すると同期されます。
この背後にあるクライアントテクノロジーとReact Nativeのデモの詳細については、このビデオをご覧ください。
フィードバック
ライブラリの開発チームは、これらのライブラリとサービスがどのように役立っているかを知りたいと思っています。また、ReactとReact Nativeでクラウドサービスをより簡単に開発するために、他に何ができるかについても知りたいと思っています。AWS AmplifyまたはAWS AppSyncについて、GitHubのAWS Mobileチームにご連絡ください。