Skip to content

Instantly share code, notes, and snippets.

@aem
Last active December 30, 2019 19:15
Show Gist options
  • Save aem/13cca3ad082ea3aa7cd30ead85c56cac to your computer and use it in GitHub Desktop.
Save aem/13cca3ad082ea3aa7cd30ead85c56cac to your computer and use it in GitHub Desktop.
HubSpot React Native Architecture
// Inside of a Dagger module
@Singleton
@Provides
fun provideReactInstanceManager(context: Context): ReactInstanceManager {
return ReactInstanceManager.builder()
.setApplication(HubspotOneApp.get(context))
.addPackage(MainReactPackage())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(BEFORE_RESUME)
.setJSMainModuleName("index.android")
.setBundleAssetName("index.android.bundle")
.build()
}
@Inject
lateinit var reactInstanceManager: ReactInstanceManager
@Inject
lateinit var reactEventDispatcher: ReactEventDispatcher
/* ... */
private fun processJavaScript() {
reactEventDispatcher
.dispatchEvent("DoSomething", WritableMap())
.subscribe { results: ReadableMap ->
// handle results
}
}
/* ... */
private fun
v(ReactRootView::class.java) {
init {
Anvil.currentView<ReactRootView>().startReactApplication(reactInstanceManager, "MyComponent", null)
reactInstanceManager.onHostResume(rootActivity, rootActivity)
}
}
import React from 'react'
import { DeviceEventEmitter, NativeModules } from 'react-native'
const addListener = (event, callback) => {
DeviceEventEmitter.addListener(event, ({key, data}) => {
// `key` is the numeric key assigned by the native dispatcher,
// `data` contains the data that the native call actually wanted to pass
NativeModules.ReactResultRouter.routeResults({
key,
data: callback(data)
})
})
}
export default class NativeEventDispatcher extends React.Component {
componentDidMount() {
addListener('DoSomething', ({prop1, prop2}) => {
// prop1 and prop2 are params passed in from native code
return doSomething(prop1, prop2)
})
}
render() {
return null
}
}
@Singleton
class ReactEventDispatcher @Inject constructor(val reactInstanceManager: ReactInstanceManager) {
private val callResults = PublishSubject<ReadableMap>()
private var currentKey: Long = 0L
private fun nextKey(): Long {
currentKey += 1
return currentKey
}
fun route(callResult: ReadableMap) {
callResults.onNext(callResult)
}
fun resultObservable(key: Long): Observable<ReadableMap> {
return callResults.filter { it.getString("key").toLong() == key }
}
fun dispatchEvent(event: String, data: WritableMap): Observable<ReadableMap> {
val key = nextKey()
val eventData = WritableNativeMap()
eventData.putString("key", key.toString())
eventData.putMap("data", data)
reactInstanceManager.currentReactContext
?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
?.emit(event, eventData)
return resultObservable(key)
.serialize()
.observeOn(AndroidSchedulers.mainThread())
.map { it.getMap("data") }
}
}
class ReactResultRouter(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
@Inject lateinit var reactEventDispatcher: ReactEventDispatcher
init {
HubspotOneApp.get(reactContext.baseContext).component().inject(this)
}
override fun getName(): String = "ReactResultRouter"
@ReactMethod
fun routeResults(results: ReadableMap) {
reactEventDispatcher.route(results)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment