Skip to content

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.

Copy link

commented Apr 23, 2018

THIS CHEATSHEET IS AWESOME

@comann

This comment has been minimized.

Copy link

commented May 17, 2018

Huge Help.

@AndreiCalazans

This comment has been minimized.

Copy link

commented May 28, 2018

Great man!!!

@CrazyPython

This comment has been minimized.

Copy link

commented Jun 10, 2018

Can you add some swift examples?

@ednew2018

This comment has been minimized.

Copy link

commented Jun 12, 2018

Good stuff!!!

@hqdai

This comment has been minimized.

Copy link

commented Jul 2, 2018

Thank you so much ;)

@code-matt

This comment has been minimized.

Copy link

commented Sep 17, 2018

+100

@austinamorusocfc

This comment has been minimized.

Copy link

commented Sep 19, 2018

Yes

@tashbenbetov

This comment has been minimized.

Copy link

commented Oct 1, 2018

Robin, thank you!

@phatmann

This comment has been minimized.

Copy link

commented Nov 3, 2018

Another way to communicate from native to JS is to use RCTRootView's appProperties property.

@ccorcos

This comment has been minimized.

Copy link

commented Dec 6, 2018

Very nice. One thing I'm trying to do that I haven't figured out yet (would be a nice addition to this sheet) is callbacks into JS from Native. For example:

<NativeComponent
  shouldDoSomething={async args => {
    const doIt = await compute(args)
    return doIt
  }}
/>
@LukaGiorgadze

This comment has been minimized.

Copy link

commented Mar 4, 2019

Can you add some swift examples?

Swift Example:
VideoPlayer.swift

import Foundation

@objc(VideoPlayer)
class VideoPlayer: NSObject {
  
  @objc(time:)
  func seekTo(_ time: Double) {
    // seek function
  }
  
}
@fpang0502

This comment has been minimized.

Copy link

commented Jun 5, 2019

Is it possible to bridge a Swift ViewController to be displayed in ReactNative?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.