Persistent bridge between a React VR component and native module code

百般思念 提交于 2019-12-19 11:41:14

问题


I am trying to have my browser send in some DOM events into the React VR components.

The closest I got is this code using "native modules."

(client.js)

const windowEventsModule = new WindowEventsModule();

function init(bundle, parent, options) {
  const vr = new VRInstance(bundle, 'WelcomeToVR', parent, {
    ...options,
    nativeModules: [windowEventsModule]
  });

  windowEventsModule.init(vr.rootView.context);

  vr.start();

  return vr;
}

window.ReactVR = {init};

(WindowEventsModule.js)

export default class WindowEventsModule extends Module {
  constructor() {
    super('WindowEventsModule');

    this.listeners = {};

    window.onmousewheel = event => {
      this._emit('onmousewheel', event);
    };
  }

  init(rnctx) {
    this._rnctx = rnctx;
  }

  _emit(name, ob) {
    if (!this._rnctx) {
      return;
    }

    Object.keys(this.listeners).forEach(key => {
      this._rnctx.invokeCallback(this.listeners[key], [ob]);
    });
  }

  onMouseWheel(listener) {
    const key = String(Math.random());
    this.listeners[key] = listener;
    return () => {
      delete this.listeners[key];
    };
  }
}

So my components can now call WindowEvents.onMouseWheel(function() {}), and get a callback from the DOM world.

Unfortunately, this only works once. RN will apparently invalidate my callback after it is called.

I also investigated this._rnctx.callFunction(), which can call an arbitrary function on something called "callable module". I don't see how I can get from there to my components.

Is there something I am missing? What's the pattern to feed arbitrary messages from the native world into the ReactVR background worker?


回答1:


I figured it out... sort of.

The trick was to create my own "BatchedBridge", which I can then reach using the callFunction() from the context.

(index.vr.js)

import React from 'react';
import {AppRegistry, asset, Pano, View} from 'react-vr';
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);

export default class WelcomeToVR extends React.Component {
    constructor(props) {
        super(props);

        this.onBrowserEvent = this.onBrowserEvent.bind(this);
    }

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

    onBrowserEvent(name, event) {
        // Do the thing here
    }

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

    render() {
        //...
    }
};

AppRegistry.registerComponent('WelcomeToVR', () => WelcomeToVR);

(WindowEventsModule.js)

import {Module} from 'react-vr-web';
import lodash from 'lodash';

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

export default class WindowEventsModule extends Module {
    constructor() {
        super('WindowEventsModule');

        this._bridgeName = 'BrowserBridge';

        window.onmousewheel = event => {
            this._emit('onmousewheel', event);
        };
    }

    init(rnctx) {
        this._rnctx = rnctx;
    }

    _emit(name, event) {
        if (!this._rnctx) {
            return;
        }

        const eventOb = eventToOb(event);

        this._rnctx.callFunction(this._bridgeName, 'notifyEvent', [name, eventOb]);
    }
}

This feels very hacky, as it doesn't seem BatchedBridge was ever meant to be exposed to consumers.

But until there is a better option, I think I'll go with this.



来源:https://stackoverflow.com/questions/44394508/persistent-bridge-between-a-react-vr-component-and-native-module-code

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!