Search code examples
javascriptreactjsreact-nativereact-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...


Solution

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