Постоянный мост между компонентом React VR и собственным кодом модуля
Я пытаюсь, чтобы мой браузер отправлял некоторые события DOM в компоненты React VR.
Наиболее близким мне стал этот код, использующий "нативные модули".
(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];
};
}
}
Так что мои компоненты теперь могут вызывать WindowEvents.onMouseWheel(function() {})
и получить обратный звонок из мира DOM.
К сожалению, это работает только один раз. RN, очевидно, сделает недействительным мой обратный вызов после его вызова.
Я также исследовал this._rnctx.callFunction()
, который может вызывать произвольную функцию для того, что называется "вызываемый модуль". Я не понимаю, как я могу добраться оттуда до моих компонентов.
Есть что-то, чего мне не хватает? Каков шаблон для подачи произвольных сообщений из родного мира в фоновый рабочий ReactVR?
1 ответ
Я понял это... вроде.
Хитрость заключалась в том, чтобы создать свой собственный "BatchedBridge", который я могу затем получить с помощью callFunction()
из контекста.
(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]);
}
}
Это чувствует себя очень взволнованным, поскольку это не кажется BatchedBridge
когда-либо предназначался, чтобы быть выставленным потребителям.
Но пока не найдется лучшего варианта, думаю, я пойду с этим.