【React Native】文件翻譯閱讀紀錄 - 指南(iOS) - Native 和 React Native之間的通信
Facebook Open Source React Native |
在“與現有應用程序集成指南”和“本機UI組件”指南中,我們將學習如何在本機組件中嵌入React Native,反之亦然。當我們混合使用Native和React Native組件時,我們最終會發現需要在這兩個世界之間進行通信。其他指南中已經提到了實現這一目標的一些方法。本文總結了可用的技術。
介紹
React Native 的靈感來自 React,因此信息流的基本思想是相似的。 React 中的流程是單向的。我們維護組件的層次結構,其中每個組件僅依賴於其父級和它自己的內部狀態。我們使用屬性執行此操作:數據以自上而下的方式從父級傳遞給其子級。如果祖先組件依賴於其後代的狀態,則應傳遞回調以供後代使用以更新祖先。
相同的概念適用於 React Native。只要我們在框架內構建我們的應用程序,我們就可以使用屬性和回調驅動我們的應用程序。但是,當我們混合使用 React Native 和本機組件時,我們需要一些特殊的跨語言機制來允許我們在它們之間傳遞信息。
相同的概念適用於 React Native。只要我們在框架內構建我們的應用程序,我們就可以使用屬性和回調驅動我們的應用程序。但是,當我們混合使用 React Native 和本機組件時,我們需要一些特殊的跨語言機制來允許我們在它們之間傳遞信息。
屬性
屬性是跨組件通信的最簡單方式。因此,我們需要一種方法將屬性從本機傳遞到React Native,從React Native傳遞到本機。
將屬性從本機傳遞到React Native
為了在本機組件中嵌入React Native視圖,我們使用RCTRootView。 RCTRootView是一個包含React Native應用程序的UIView。它還提供本機端和託管應用程序之間的接口。
RCTRootView有一個初始化程序,允許您將任意屬性傳遞給React Native應用程序。 initialProperties參數必須是NSDictionary的一個實例。字典在內部轉換為頂級JS組件可以引用的JSON對象。
將屬性從本機傳遞到React Native
為了在本機組件中嵌入React Native視圖,我們使用RCTRootView。 RCTRootView是一個包含React Native應用程序的UIView。它還提供本機端和託管應用程序之間的接口。
RCTRootView有一個初始化程序,允許您將任意屬性傳遞給React Native應用程序。 initialProperties參數必須是NSDictionary的一個實例。字典在內部轉換為頂級JS組件可以引用的JSON對象。
NSArray *imageList = @[@"http://foo.com/bar1.png",
@"http://foo.com/bar2.png"];
NSDictionary *props = @{@"images" : imageList};
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"ImageBrowserApp"
initialProperties:props];
import React from 'react';
import {
AppRegistry,
View,
Image
} from 'react-native';
class ImageBrowserApp extends React.Component {
renderImage(imgURI) {
return (
<Image source={{uri: imgURI}} />
);
}
render() {
return (
<View>
{this.props.images.map(this.renderImage)}
</View>
);
}
}
AppRegistry.registerComponent('AwesomeProject', () => ImageBrowserApp);
AppRegistry.registerComponent('AwesomeProject',()=> ImageBrowserApp);
RCTRootView還提供了一個讀寫屬性appProperties。設置appProperties後,將使用新屬性重新呈現React Native應用程序。僅當新更新的屬性與先前的屬性不同時才執行更新。
RCTRootView還提供了一個讀寫屬性appProperties。設置appProperties後,將使用新屬性重新呈現React Native應用程序。僅當新更新的屬性與先前的屬性不同時才執行更新。
NSArray *imageList = @[@"http://foo.com/bar3.png",
@"http://foo.com/bar4.png"];
rootView.appProperties = @{@"images" : imageList};
可以隨時更新屬性。但是,必須在主線程上執行更新。你在任何線程上使用getter。
一次只能更新幾個屬性。我們建議您將其構建到自己的包裝器中。
一次只能更新幾個屬性。我們建議您將其構建到自己的包裝器中。
注意:目前,在更新prop後,將不會調用頂級RN組件的JS函數componentWillReceiveProps和componentWillUpdateProps。但是,您可以在componentWillMount函數中訪問新的props。
將屬性從React Native傳遞到本機
本文詳細介紹了暴露本機組件屬性的問題。簡而言之,在自定義本機組件中使用RCT_CUSTOM_VIEW_PROPERTY宏導出屬性,然後在React Native中使用它們,就好像該組件是普通的React Native組件一樣。
屬性限制
跨語言屬性的主要缺點是它們不支持回調,這將允許我們處理自下而上的數據綁定。想像一下,由於JS操作,您希望從本機父視圖中刪除一個小RN視圖。道具沒有辦法做到這一點,因為信息需要自下而上。
雖然我們有一種跨語言的回調(這裡描述),但這些回調並不總是我們需要的。主要問題是它們不打算作為屬性傳遞。相反,這種機制允許我們從JS觸發本機操作,並在JS中處理該操作的結果。
雖然我們有一種跨語言的回調(這裡描述),但這些回調並不總是我們需要的。主要問題是它們不打算作為屬性傳遞。相反,這種機制允許我們從JS觸發本機操作,並在JS中處理該操作的結果。
跨語言交互的其他方式(事件和本機模塊)
如前一章所述,使用屬性有一些限制。有時屬性不足以推動我們的應用程序的邏輯,我們需要一個提供更多靈活性的解決方案。本章介紹React Native中可用的其他通信技術。它們可用於內部通信(在RN和RN中的本機層之間)以及外部通信(在RN與應用程序的“純本機”部分之間)。
React Native使您可以執行跨語言函數調用。您可以從JS執行自定義本機代碼,反之亦然。不幸的是,根據我們正在努力的方面,我們以不同的方式實現相同的目標。對於native - 我們使用事件機制來安排在JS中執行處理函數,而對於React Native,我們直接調用由本機模塊導出的方法。
React Native使您可以執行跨語言函數調用。您可以從JS執行自定義本機代碼,反之亦然。不幸的是,根據我們正在努力的方面,我們以不同的方式實現相同的目標。對於native - 我們使用事件機制來安排在JS中執行處理函數,而對於React Native,我們直接調用由本機模塊導出的方法。
從本機(事件)調用React Native函數
本文詳細介紹了事件。請注意,使用事件不能保證執行時間,因為事件是在單獨的線程上處理的。
事件很強大,因為它們允許我們更改React Native組件而無需引用它們。但是,使用它們時可能會遇到一些陷阱:
事件很強大,因為它們允許我們更改React Native組件而無需引用它們。但是,使用它們時可能會遇到一些陷阱:
- 由於事件可以從任何地方發送,因此可以在項目中引入意大利面風格的依賴項。
- 事件共享命名空間,這意味著您可能會遇到一些名稱衝突。不會靜態檢測到衝突,這使得它們難以調試。
- 如果您使用相同React Native組件的多個實例,並且希望將它們與事件的視角區分開來,則可能需要引入標識符並將它們與事件一起傳遞(您可以使用本機視圖的reactTag作為標識符) 。
我們在React Native中嵌入native時使用的常見模式是使本機組件的RCTViewManager成為視圖的委託,通過網橋將事件發送回JavaScript。這使相關的事件調用保持在一個地方。
從React Native調用本機函數(本機模塊)
本機模塊是JS中可用的Objective-C類。通常,每個JS橋都會創建一個每個模塊的實例。他們可以將任意函數和常量導出到React Native。本文詳細介紹了它們。
原生模塊是單例的事實限制了嵌入環境中的機制。假設我們在本機視圖中嵌入了React Native組件,並且我們想要更新本機父視圖。使用本機模塊機制,我們將導出一個函數,該函數不僅包含預期的參數,還包含父本機視圖的標識符。標識符將用於檢索對要更新的父視圖的引用。也就是說,我們需要在模塊中保持從標識符到本機視圖的映射。
儘管此解決方案很複雜,但它在RCTUIManager中使用,RCTUIManager是一個管理所有React Native視圖的內部React Native類。
本機模塊也可用於將現有本機庫公開給JS。地理位置庫是這個想法的活生生的例子。
原生模塊是單例的事實限制了嵌入環境中的機制。假設我們在本機視圖中嵌入了React Native組件,並且我們想要更新本機父視圖。使用本機模塊機制,我們將導出一個函數,該函數不僅包含預期的參數,還包含父本機視圖的標識符。標識符將用於檢索對要更新的父視圖的引用。也就是說,我們需要在模塊中保持從標識符到本機視圖的映射。
儘管此解決方案很複雜,但它在RCTUIManager中使用,RCTUIManager是一個管理所有React Native視圖的內部React Native類。
本機模塊也可用於將現有本機庫公開給JS。地理位置庫是這個想法的活生生的例子。
警告:所有本機模塊共享相同的命名空間。創建新名稱時要注意名稱衝突。
佈局計算流程
在集成native和React Native時,我們還需要一種方法來整合兩個不同的佈局系統。本節介紹常見的佈局問題,並簡要介紹解決這些問題的機制。
React Native 中嵌入的本機組件的佈局
本文將介紹此案例。基本上,由於我們所有的本機反應視圖都是UIView的子類,因此大多數樣式和大小屬性都可以像開箱即用的那樣工作。
嵌入本機的React Native組件的佈局
使用固定大小的React Native內容
最簡單的方案是當我們有一個具有固定大小的React Native應用程序時,本機方面已知。特別是,全屏React Native視圖屬於這種情況。如果我們想要一個較小的根視圖,我們可以顯式設置RCTRootView的框架。
例如,要使RN app 200(邏輯)像素為高,並且託管視圖的寬度為寬,我們可以:
// SomeViewController.m
- (void)viewDidLoad
{
[...]
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:appName
initialProperties:props];
rootView.frame = CGRectMake(0, 0, self.view.width, 200);
[self.view addSubview:rootView];
}
當我們有一個固定大小的根視圖時,我們需要尊重它在JS方面的界限。換句話說,我們需要確保React Native內容可以包含在固定大小的根視圖中。確保這一點的最簡單方法是使用flexbox佈局。如果使用絕對定位,並且React組件在根視圖的邊界外可見,則會與本機視圖重疊,從而導致某些功能出現意外行為。例如,'TouchableHighlight'不會突出顯示根視圖邊界之外的觸摸。
通過重新設置其frame屬性來動態更新根視圖的大小是完全正確的。 React Native將負責內容的佈局。
通過重新設置其frame屬性來動態更新根視圖的大小是完全正確的。 React Native將負責內容的佈局。
具有靈活大小的 React Native 內容
在某些情況下,我們想要呈現最初未知大小的內容。假設大小將在JS中動態定義。我們有兩個解決這個問題的方法。
- 您可以將React Native視圖包裝在ScrollView組件中。這可以保證您的內容始終可用,並且不會與本機視圖重疊。
- React Native允許您在JS中確定RN應用程序的大小,並將其提供給託管RCTRootView的所有者。然後,所有者負責重新佈置子視圖並保持UI一致。我們通過RCTRootView的靈活模式實現了這一目標。
RCTRootView
支持4種不同大小的靈活模式:// RCTRootView.h
typedef NS_ENUM(NSInteger, RCTRootViewSizeFlexibility) {
RCTRootViewSizeFlexibilityNone = 0,
RCTRootViewSizeFlexibilityWidth,
RCTRootViewSizeFlexibilityHeight,
RCTRootViewSizeFlexibilityWidthAndHeight,
};
RCTRootViewSizeFlexibilityNone
是默認值,它使根視圖的大小固定(但仍可以使用setFrame更新:)。其他三種模式允許我們跟踪React Native內容的大小更新。例如,將模式設置為RCTRootViewSizeFlexibilityHeight將導致React Native測量內容的高度並將該信息傳遞回RCTRootView的委託。可以在委託中執行任意操作,包括設置根視圖的框架,以使內容適合。只有在內容大小發生變化時才會調用委託。警告:在JS和native中創建一個靈活的維度會導致未定義的行為。例如 - 當您在託管RCTRootView上使用RCTRootViewSizeFlexibilityWidth時,不要使頂級React組件的寬度靈活(使用flexbox)。
我們來看一個例子。
// FlexibleSizeExampleView.m
- (instancetype)initWithFrame:(CGRect)frame
{
[...]
_rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"FlexibilityExampleApp"
initialProperties:@{}];
_rootView.delegate = self;
_rootView.sizeFlexibility = RCTRootViewSizeFlexibilityHeight;
_rootView.frame = CGRectMake(0, 0, self.frame.size.width, 0);
}
#pragma mark - RCTRootViewDelegate
- (void)rootViewDidChangeIntrinsicSize:(RCTRootView *)rootView
{
CGRect newFrame = rootView.frame;
newFrame.size = rootView.intrinsicContentSize;
rootView.frame = newFrame;
}
在示例中,我們有一個包含根視圖的FlexibleSizeExampleView視圖。我們創建根視圖,初始化它並設置委託。代表將處理大小更新。然後,我們將根視圖的大小靈活性設置為RCTRootViewSizeFlexibilityHeight,這意味著每次React Native內容更改其高度時都將調用rootViewDidChangeIntrinsicSize:方法。最後,我們設置根視圖的寬度和位置。請注意,我們也設置了高度,但它沒有效果,因為我們使高度RN依賴。
您可以在此處查看示例的完整源代碼。
您可以在此處查看示例的完整源代碼。
可以動態更改根視圖的大小靈活性模式。更改根視圖的靈活性模式將安排佈局重新計算,並且一旦知道內容大小,將調用委託rootViewDidChangeIntrinsicSize:方法。
注意:React Native佈局計算在特殊線程上執行,而本機UI視圖更新在主線程上完成。這可能會導致本機和React Native之間的UI不一致。這是一個已知問題,我們的團隊正在努力同步來自不同來源的UI更新。
注意:在根視圖成為某些其他視圖的子視圖之前,React Native不會執行任何佈局計算。如果要隱藏React Native視圖直到其尺寸已知,請將根視圖添加為子視圖並使其最初隱藏(使用UIView的隱藏屬性)。然後在委託方法中更改其可見性。
0 意見