Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save fanky10/cb4445793a12fe82af7ab267dc93b2e0 to your computer and use it in GitHub Desktop.
Save fanky10/cb4445793a12fe82af7ab267dc93b2e0 to your computer and use it in GitHub Desktop.
React Native Bridging Cheatsheet

Callbacks (getting a response from js)

React

// index.ios.js
videoPlayer.seekTo(100, (error, someData) => {
  if (error) {
    console.error(error)
  } else {
    console.log(someData)
  }
})

// index.android.js
videoPlayer.seekTo(
  100, 
  (msg) => {
    console.error(msg)
  }, 
  (someData) => {
    console.log(someData)
  }
)

iOS

docs

RCT_EXPORT_METHOD(seekTo:(double)time callback:(RCTResponseSenderBlock)callback)
{
  NSArray *someData;
  callback(@[[NSNull null], someData]); // (error, someData) in js
}

Android

docs

@ReactMethod
public void seekTo(double time, Callback errorCallback, Callback successCallback) {
  try {
    successCallback.invoke(someData);
  } catch (Exception e) {
    errorCallback.invoke(e.getMessage());
  }
}

Events

Keep in mind:

  • Events share namespace (so come up with an app-wide naming convention)
  • If you have several instances of a component, you need to pass an identifier (like a view's reactTag) to identify it in the handler. On the js end, reactTag maps to rootTag.

React

import { NativeEventEmitter, NativeModules } from 'react-native'
const videoPlayer = NativeModules.VideoPlayer
const videoPlayerEmitter = new NativeEventEmitter(VideoPlayer)
const subscription = videoPlayerEmitter.addListener('video-progress', (progress) => console.log(progress))

// Don't forget to unsubscribe, typically in `componentWillUnmount`
subscription.remove()

iOS

docs

//

Promises

React

async function loadVideo() {
  try {
    var videoLoaded = await VideoPlayer.loadVideo();
    this.setState(videoLoaded: videoLoaded)
  } catch (e) {
    console.error(e)
  }
}

loadVideo()

iOS

docs

RCT_REMAP_METHOD(loadVideo, loadVideoWithResolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
  BOOL videoLoaded = ...; // could be any data type listed under https://facebook.github.io/react-native/docs/native-modules-ios.html#argument-types
  if (videoLoaded) {
    resolve(videoLoaded);
  } else {
    NSError *error = ...
    reject(@"error", @"error description", error);
  }
}

Android

docs

@ReactMethod loadVideo(Promise promise) {
  try {
    Boolean videoLoaded = ...; // could be any data type listed under https://facebook.github.io/react-native/docs/native-modules-android.html#argument-types
    if (videoLoaded) {
      promise.resolve(videoLoaded);
    }
  } catch (Exception e) {
    promise.reject(ERROR, e);
  }
}

Properties (for UI)

React

render() {
  <VideoPlayer loop={false} />
}

VideoPlayer.propTypes = {
  loop: PropTypes.bool,
}

VideoPlayer.defaultProps = {
  loop: false,
}

iOS

docs

// BMEVideoManager.m
RCT_EXPORT_VIEW_PROPERTY(loop, BOOL);

Android

docs

@ReactProp(name = "loop")
public void setLoop(Boolean loop) {
  ...
}

Simple one-off example

React

import { NativeModules } from 'react-native'
const videoPlayer = NativeModules.VideoPlayer
videoPlayer.seekTo(100)

iOS

Keep in mind:

  • RCT_EXPORT_METHOD exposes the name of the method up to the first colon in js land.
  • When many methods share the same name, use RCT_REMAP_METHOD to define a different name for js and map it to the native method. ie. RCT_REMAP_METHOD(differentMethodName, methodName:(BOOL)arg1 arg2:(BOOL)arg2).

docs

// VideoPlayer.h
#import <React/RCTBridgeModule.h>

@interface VideoPlayer : NSObject <RCTBridgeModule>
@end

@implementation VideoPlayer
RCT_EXPORT_MODULE(); // or RCT_EXPLORT_MODULE(AwesomeVideoPlayer) -- custom name

RCT_EXPORT_METHOD(seekTo:(double)time)
{
  // seek to time
}
@end

Android

docs

// VideoPlayer.java
package com.beme.react

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;

public class VideoModule extends ReactContextBaseJavaModule {
  public VideoModule(ReactApplicationContext reactContext) {
    super(reactContext)
  }

  @Override
  public String getName() {
    return "VideoPlayer";
  }

  @ReactMethod
  public void seekTo(double time) {
    // seek to time
  }
}

Register the module

// VideoPackage.java
package com.beme.react

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

public class VideoPackage implements ReactPackage {
  @Override
  public List<Class<? extends JavaScriptModule>> createJSModules() {
    return Collections.emptyList();
  }

  @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 VideoModule(reactContext));
    return modules;
  }
}
// MainApplication.java
protected List<ReactPackage> getPackages() {
    return Arrays.<ReactPackage>asList(
            new MainReactPackage(),
            new VideoPackage()); // <-- Add this line with your package name.
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment