@davguij // guijarro.dav@gmail.com
Transformed into native components
More https://reactnative.dev/docs/components-and-apis
import React from 'react';
import { Text, View } from 'react-native';
const HelloWorldApp = () => {
return (
<View style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}}>
<Text>Hello, world!</Text>
</View>
);
}
export default HelloWorldApp;Main thread (UI thread)
JavaScript thread
Shadow thread
JavaScript thread
Shadow thread
Native side
Event
React Native consumer app
iOS
Android
JavaScript facade
npx react-native-builder-bob init
Getting started
JavaScript facade
import { NativeModules } from "react-native";
const { MyAwesomeModule } = NativeModules;
interface MyAwesomeModuleInterface {
doSomethingCool(oneArgument: string, anotherArgument: boolean): void;
}
export default MyAwesomeModule as MyAwesomeModuleInterface;
Android implementation
// android/app/src/main/java/com/davguij/my-awesome-module/MyAwesomeModule.java
package com.davguij.my-awesome-module;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import java.util.Map;
import java.util.HashMap;
public class MyAwesomeModule extends ReactContextBaseJavaModule {
// constructor
MyAwesomeModule(ReactApplicationContext context) {
super(context);
}
}Android: getName()
// android/app/src/main/java/com/davguij/my-awesome-module/MyAwesomeModule.java
(...)
@Override
public String getName() {
return "MyAwesomeModule";
}
(...)Android: expose a method
// android/app/src/main/java/com/davguij/my-awesome-module/MyAwesomeModule.java
(...)
@ReactMethod
public void doSomethingCool(String oneArgument, Boolean anotherArgument) {
// Implementation!
}
(...)Android: register the module
// android/app/src/main/java/com/davguij/
// my-awesome-module/MyAwesomePackage.java
package com.davguij.my-awesome-module;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class MyAwesomePackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(
ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new MyAwesomePackage(reactContext));
return modules;
}
}// android/app/src/main/java/com/davguij/
// my-awesome-module/MainApplication.java
import com.davguij.my-awesome-module.MyAwesomePackage;
(...)
@Override
protected List<ReactPackage> getPackages() {
List<ReactPackage> packages = new PackageList(this).getPackages();
packages.add(new MyAwesomePackage()); // <---
return packages;
}
};
Android: instantiate the module
Done!
iOS implementation
// ios/MyAwesomeModule/MyAwesomeModule.h
#import <React/RCTBridgeModule.h>
@interface MyAwesomeModule : NSObject <RCTBridgeModule>
@end
---
// ios/MyAwesomeModule/MyAwesomeModule.m
#import "MyAwesomeModule.h"
@implementation MyAwesomeModule
RCT_EXPORT_MODULE();
@end
iOS: Custom module name
@implementation MyAwesomeModule
// export the name of the native module as
// 'MyAwesomeModule' since no explicit name is mentioned
RCT_EXPORT_MODULE();
// export the module using a custom name
RCT_EXPORT_MODULE(MyIncredibleModule);
iOS: expose a method
// ios/MyAwesomeModule/MyAwesomeModule.m
#import "MyAwesomeModule.h"
@implementation MyAwesomeModule
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(doSomethingCool:(NSString *)oneArg anotherArg:(BOOL *)anotherArg)
{
// do your magic here!
}
@end
Done!
Distribution
npm package
Public
Private
@ namespaced
Registry (npm, GitHub...)
Build, version and release
yarn publish
npm run publishrelease-it
Test
Build
Bump version
Create Git tag
Generate changelog
Publish
Android: create/import a View
// MyAwesomeView.java
public class MyAwesomeView extends RelativeLayout {
(...)
}Android: implement the ViewManager
// MyAwesomeViewManager.java
package com.davguij.my-awesome-ui-component
import com.facebook.react.uimanager.SimpleViewManager
public class MyAwesomeViewManager extends SimpleViewManager<MyAwesomeView> {
public static final String COMPONENT_NAME = "MyAwesomeComponent";
@Override
public String getName() {
return REACT_CLASS;
}
}Android: implement createViewInstace()
// MyAwesomeViewManager.java
(...)
import com.facebook.react.uimanager.ThemedReactContext
public class MyAwesomeViewManager extends SimpleViewManager<MyAwesomeView> {
(...)
@Override
public MyAwesomeView createViewInstance(ThemedReactContext reactContext) {
return new MyAwesomeView(reactContext);
}
}Android: expose props setters
// MyAwesomeViewManager.java
(...)
public class MyAwesomeViewManager extends SimpleViewManager<MyAwesomeView> {
(...)
@ReactProp(name = "floatProp", defaultFloat = 0f)
public void setFloatProp(MyAwesomeView view, float floatProp) {
// do the magic!
}
}Android: register the ViewManager
// MyAwesomePackage.java
package com.davguij.my-awesome-module;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
public class MyAwesomePackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(
ReactApplicationContext reactContext) {
return Arrays.<ViewManager>asList(
new MyAwesomeViewManager(reactContext)
);
}
(...)
}Android: Done!
iOS: extend the React ViewManager class
// MyAwesomeViewManager.m
#import <React/RCTViewManager.h>
@interface MyAwesomeViewManager : RCTViewManager
@end
iOS: expose the module
// MyAwesomeViewManager.m
(...)
@implementation MyAwesomeViewManager
RCT_EXPORT_MODULE(MyAwesomeView)
@end
iOS: implement the view() method
// MyAwesomeViewManager.m
(...)
@implementation MyAwesomeViewManager
RCT_EXPORT_MODULE(MyAwesomeView) // Used later to refer to the component
- (UIView *)view
{
return [[MyAwesomeView alloc] init];
}
@end
iOS: expose prop setters
// MyAwesomeViewManager.m
(...)
@implementation MyAwesomeViewManager
(...)
RCT_EXPORT_VIEW_PROPERTY(isReallyAwesome, BOOL)
@end
iOS: Done!
Wrap it with JavaScript
// MyAwesomeView.ts
import React from 'react';
import { requireNativeComponent, ViewProps } from 'react-native';
interface NativeComponentProps {
isReallyAwesome: boolean;
}
const MyAwesomeViewRaw = requireNativeComponent<NativeComponentProps>('MyAwesomeView');
type MyAwesomeViewProps = ViewProps && NativeComponentProps;
export const MyAwesomeView: React.FC<MyAwesomeViewProps> = (props) => {
return <MyAwesomeViewRaw {...props} />;
}
Directory structure
💡 Use Git submodules!
💡create-react-native-app
Install JS dependencies
yarn add react-native reactInstall native dependencies
// build.gradle
dependencies {
(...)
implementation "com.facebook.react:react-native:+"
implementation "org.webkit:android-jsc:+"
(...)
}
allprojects {
repositories {
maven {
// All of React Native (JS, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"
}
maven {
// Android JSC is installed from npm
url("$rootDir/../node_modules/jsc-android/dist")
}
(...)
}
(...)
}
Allow non-https traffic
// AndroidManifest.xml
<!-- ... -->
<application
android:usesCleartextTraffic="true" tools:targetApi="28" >
<!-- ... -->
</application>
<!-- ... -->
For the app to connect to the Metro server and be served the RN code
Build RN view
// index.js
import React from 'react';
import {
AppRegistry,
Text,
View
} from 'react-native';
class MyAwesomeRNApp extends React.Component {
render() {
return (
<View>
<Text>
Awesome, right?
</Text>
</View>
);
}
}
AppRegistry.registerComponent(
'MyAwesomeRNApp',
() => MyAwesomeRNApp
);
The magic: ReactRootView
public class MyReactActivity extends Activity implements DefaultHardwareBackBtnHandler {
private ReactRootView mReactRootView;
private ReactInstanceManager mReactInstanceManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
(...)
mReactRootView = new ReactRootView(this);
List<ReactPackage> packages = new PackageList(getApplication()).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
// Remember to include them in `settings.gradle` and `app/build.gradle` too.
mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setCurrentActivity(this)
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("index")
.addPackages(packages)
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
mReactRootView.startReactApplication(mReactInstanceManager, "MyAwesomeRNApp", null);
setContentView(mReactRootView);
}
@Override
public void invokeDefaultOnBackPressed() {
super.onBackPressed();
}
}export default function isDeviceCompatible() {
DeviceCompatibilityBridge.isBackCameraCompatible((err, isCompat) => {
// callback
if (isCompat === false) {
AnalyticsSrv.emit('UNSUPPORTED_CAMERA', {caseUuid, deviceInfo});
(...)
}
});
}package api_client;
public class DeviceCompatibilityBridgeModule extends ReactContextBaseJavaModule {
(...)
@ReactMethod
public void isBackCameraCompatible (Callback callback) {
try {
final String[] cameraList = cameraManager.getCameraIdList();
for (final String cameraId : cameraList) {
final CameraCharacteristics chars
= cameraManager.getCameraCharacteristics(cameraId);
final Integer facing = chars.get(CameraCharacteristics.LENS_FACING);
if(facing == CameraCharacteristics.LENS_FACING_BACK){
final boolean hasProfile = CamcorderProfile.hasProfile(Integer.parseInt(cameraId));
if (!hasProfile && cameraList.length > 1) {
continue;
}
CamcorderProfile backCameraProfile = CamcorderProfile.get(Integer.parseInt(cameraId));
if(backCameraProfile.hasProfile(CamcorderProfile.QUALITY_720P) && backCameraProfile.get(CamcorderProfile.QUALITY_720P).videoFrameRate > 23){
callback.invoke(null, true);
return;
}
}
}
} catch (Exception e) {
e.printStackTrace();
callback.invoke(e.getMessage());
return;
}
callback.invoke(null, false);
}
}No more Bridge!