Instantly share code, notes, and snippets.

Embed
What would you like to do?
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());
  }
}

Go to Top

Events (for UI components)

Keep in mind:

  • Events share namespace (so come up with an app-wide naming convention)

React

// VideoPlayer.js

class VideoPlayer extends React.PureComponent {
  _onEnd = (event) => {
    if (!this.props.onEnd) {
      return;
    }
    this.props.onEnd(event.nativeEvent)
  }
  render() {
    // Re-assign onEnd to the private _onEnd and store it in `nativeProps`
    const nativeProps = {
      ...this.props,
      onEnd: this._onEnd,
    }
    return (
      <RCTVideo
        {...nativeProps}
      />
    )
  }
}

const RCTVideo = requireNativeComponent('RCTVideo', VideoPlayer)

VideoPlayer.propTypes = {
  /**
   *  Callback that is called when the current player item ends.
   */
  onEnd: PropTypes.func,
}

iOS

docs

Notes: Bubbling events are like DOM events so that a parent component can capture an event fired by its child. Generally these are UI-related, like "the user touched this box". Direct events are not bubbled and are intended for more abstract events like "this image failed to load".

// VideoPlayer.h
#import <UIKit/UIKit.h>
#import <React/RCTView.h>

@interface VideoPlayer : UIView

// or RCTBubblingEventBlock
@property (nonatomic, copy) RCTDirectEventBlock onEnd;

@end

// VideoPlayerManager.m (inherits from RCTViewManager)
@implementation VideoPlayerManager

RCT_EXPORT_MODULE()

RCT_EXPORT_VIEW_PROPERTY(onEnd, RCTDirectEventBlock)

- (UIView *)view
{
  ...
}

/* 
 * `VideoPlayerManager` acts as the delegate of all of the `VideoPlayer` views. This is just one
 * pattern and it's perfectly fine to call `onEnd` from the `VideoPlayer` directly.
 */
- (void)onEnd:(VideoPlayer *)videoPlayer
{
  if (!videoPlayer.onEnd) {
    return;
  }
  videoPlayer.onEnd(@{ @"some-data" : @1 });
}

@end

Android

docs

example

Define a custom event mapping by overriding getExportedCustomDirectEventTypeConstants in the manager class:

// VideoPlayerManager.java
@Override
public @Nullable Map getExportedCustomDirectEventTypeConstants() {
    return MapBuilder.of(
            "onEnd",
            MapBuilder.of("registrationName", "onEnd")
    );
}

Dispatch the event:

// VideoPlayerView.java
private void dispatchOnEnd() {
  WritableMap event = Arguments.createMap();
  ...
  reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(
    getId(),
    "onEnd",
    event
  );
}

Go to Top

Events

Keep in mind:

  • Events share namespace (so come up with an app-wide naming convention)

React

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

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

iOS

docs

Important Note: Don't send events if there are no listeners attached. You will get a warning about spending unneccessary resources. Override -startObserving and -stopObserving like in the example below:

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

@interface CalendarManager : RCTEventEmitter <RCTBridgeModule>

@end

// VideoPlayer.m
#import "VideoPlayer.h"

@implementation VideoPlayer
{
  BOOL _hasListeners;
}

RCT_EXPORT_MODULE();

- (NSArray<NSString *> *)supportedEvents
{
  return @[@"video-progress"];
}

// Will be called when this module's first listener is added.
- (void)startObserving
{
  _hasListeners = YES;
}

// Will be called when this module's last listener is removed, or on dealloc.
- (void)stopObserving
{
  _hasListeners = NO;
}

- (void)onVideoProgress
{
  CGFloat progress = ...;
  if (_hasListeners) {
    [self sendEventWithName:@"video-progress" body:@{ @"progress": @(progress) }];
  }
}

@end

Android

docs

example

private void onVideoProgress() {
  WriteableMap params = Arguments.createMap();
  ...
  reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("video-progress", params);
}

Go to Top

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);
  }
}

Go to Top

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) {
  ...
}

Go to Top

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.
}

Go to Top

@dwilt

This comment has been minimized.

Show comment
Hide comment
@dwilt

dwilt Apr 23, 2018

THIS CHEATSHEET IS AWESOME

dwilt commented Apr 23, 2018

THIS CHEATSHEET IS AWESOME

@comann

This comment has been minimized.

Show comment
Hide comment
@comann

comann May 17, 2018

Huge Help.

comann commented May 17, 2018

Huge Help.

@AndreiCalazans

This comment has been minimized.

Show comment
Hide comment
@AndreiCalazans

AndreiCalazans commented May 28, 2018

Great man!!!

@CrazyPython

This comment has been minimized.

Show comment
Hide comment
@CrazyPython

CrazyPython Jun 10, 2018

Can you add some swift examples?

CrazyPython commented Jun 10, 2018

Can you add some swift examples?

@edward8628

This comment has been minimized.

Show comment
Hide comment
@edward8628

edward8628 Jun 12, 2018

Good stuff!!!

edward8628 commented Jun 12, 2018

Good stuff!!!

@hqdai

This comment has been minimized.

Show comment
Hide comment
@hqdai

hqdai Jul 2, 2018

Thank you so much ;)

hqdai commented Jul 2, 2018

Thank you so much ;)

@code-matt

This comment has been minimized.

Show comment
Hide comment
@code-matt

code-matt commented Sep 17, 2018

+100

@austinamorusocfc

This comment has been minimized.

Show comment
Hide comment
@austinamorusocfc

austinamorusocfc commented Sep 19, 2018

Yes

@tashbenbetov

This comment has been minimized.

Show comment
Hide comment
@tashbenbetov

tashbenbetov Oct 1, 2018

Robin, thank you!

tashbenbetov commented Oct 1, 2018

Robin, thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment