React-360: Calling React-360 method from Native Module?

Created on 8 Aug 2018  路  3Comments  路  Source: facebookarchive/react-360

Is it possible to call a react-360 method from a native Module?

I am working on a VR project which is has 2 user roles, a leader (who sets up and configures a VR session) and clients (who connect to this session).
I am using a Native module to perform a DOM overlay in which several buttons related to session configuration are displayed for the leader. I was wondering if it is possible to call a function within the React360 code directly from a Native Module (i.e. not as a callback as the event would originate from the Native Module)?
This could be a complete anti-pattern, I can't seem to see a way of doing it...

Thanks,
Kevin

Most helpful comment

Events are the correct way to do it, but if you use DeviceEventEmitter it can be cleaner:

https://github.com/facebook/react-360/blob/master/React360/js/Modules/ControllerInfo.js#L50-L53

and on the React 360 side:

import RCTDeviceEventEmitter from 'RCTDeviceEventEmitter';

RCTDeviceEventEmitter.addListener('eventName', (arg1, arg2, arg3) => {
  // ...
})

All 3 comments

I actually got this working with the following:

In client.js I passed the context to the DOM overlay native module:

  const r360 = new ReactInstance(bundle, parent, {
    // Add custom options here
    fullScreen: true,
    cursorVisibility: "visible",
    nativeModules: [
      // Create an instance of the DOM overlay module and pass the context
      ctx => new DashboardModule(ctx, domDashboardContainer)
    ],
    ...options,
  });

In the dashboard native module :

const eventToOb = (event) => {
    const eventOb = {};
    for (let key in event) {
        const val = event[key];
        if (!(lodash.isFunction(val) || lodash.isObject(val))) {
            eventOb[key] = val;
        }
    }
    return eventOb;
};

....

    constructor(ctx, overlayContainer) {
        super('DashboardModule');     
...
        this._rnctx = ctx;

        this._bridgeName = 'BrowserBridge';
    }

   onButtonClick() {
       ....
       this._emit('nativeButtonClicked', event);
   }

    _emit(name, event) {
        if (!this._rnctx) {
            return;
        }
        const eventOb = eventToOb(event);
        this._rnctx.callFunction(this._bridgeName, 'notifyEvent', [name, eventOb]);
    }

...

and in my index.js

...
import BatchedBridge from 'react-native/Libraries/BatchedBridge/BatchedBridge';
import lodash from 'lodash';

class BrowserBridge {
  constructor() {
      this._subscribers = {};
  }

  subscribe(handler) {
      const key = String(Math.random());
      this._subscribers[key] = handler;
      return () => {
          delete this._subscribers[key];
      };
  }

  notifyEvent(name, event) {
      lodash.forEach(this._subscribers, handler => {
          handler(name, event);
      });
  }
}

const browserBridge = new BrowserBridge();
BatchedBridge.registerCallableModule(BrowserBridge.name, browserBridge);

....

  constructor(props) {
    super(props);
    this.onBrowserEvent = this.onBrowserEvent.bind(this);
    ...
  }

  componentWillMount() {
    this.unsubscribe = browserBridge.subscribe(this.onBrowserEvent);
  }

  onBrowserEvent(name, event) {
      // Do action on event here
  }

  componentWillUnmount() {
      if (this.unsubscribe) {
          this.unsubscribe();
          delete this.unsubscribe;
      }
  }

If there is a better way of doing this please let me know.

Events are the correct way to do it, but if you use DeviceEventEmitter it can be cleaner:

https://github.com/facebook/react-360/blob/master/React360/js/Modules/ControllerInfo.js#L50-L53

and on the React 360 side:

import RCTDeviceEventEmitter from 'RCTDeviceEventEmitter';

RCTDeviceEventEmitter.addListener('eventName', (arg1, arg2, arg3) => {
  // ...
})

Great thanks for the tip. I will take a look.

Was this page helpful?
0 / 5 - 0 ratings