UNPKG

core-native

Version:

A lightweight framework based on React Native + Redux + Redux Saga, in strict TypeScript.

132 lines 6.69 kB
import React from "react"; import { AppState } from "react-native"; import { delay, call as rawCall } from "redux-saga/effects"; import { app } from "../app"; import { executeAction } from "../module"; export class ModuleProxy { constructor(module, actions) { this.module = module; this.actions = actions; } getActions() { return this.actions; } attachLifecycle(ComponentType) { var _a; const moduleName = this.module.name; const lifecycleListener = this.module; const modulePrototype = Object.getPrototypeOf(lifecycleListener); const actions = this.actions; return _a = class extends React.PureComponent { constructor(props) { super(props); this.lifecycleSagaTask = null; this.tickCount = 0; this.mountedTime = Date.now(); this.onAppStateChange = (nextAppState) => { const { appState } = this.state; if (["inactive", "background"].includes(appState) && nextAppState === "active") { if (this.hasOwnLifecycle("onAppActive")) { app.store.dispatch(actions.onAppActive()); } } else if (appState === "active" && ["inactive", "background"].includes(nextAppState)) { if (this.hasOwnLifecycle("onAppInactive")) { app.store.dispatch(actions.onAppInactive()); } } this.setState({ appState: nextAppState }); }; this.hasOwnLifecycle = (methodName) => { return Object.prototype.hasOwnProperty.call(modulePrototype, methodName); }; this.state = { appState: AppState.currentState }; } componentDidMount() { this.lifecycleSagaTask = app.sagaMiddleware.run(this.lifecycleSaga.bind(this)); // According to the document, this API may change soon // Ref: https://facebook.github.io/react-native/docs/appstate#addeventlistener AppState.addEventListener("change", this.onAppStateChange); const props = this.props; if ("navigation" in props && typeof props.navigation.addListener === "function") { if (this.hasOwnLifecycle("onFocus")) { this.unsubscribeFocus = props.navigation.addListener("focus", () => { app.store.dispatch(actions.onFocus()); }); } if (this.hasOwnLifecycle("onBlur")) { this.unsubscribeBlur = props.navigation.addListener("blur", () => { app.store.dispatch(actions.onBlur()); }); } } } componentWillUnmount() { var _a, _b, _c; if (this.hasOwnLifecycle("onDestroy")) { app.store.dispatch(actions.onDestroy()); } app.logger.info({ action: `${moduleName}/@@DESTROY`, info: { tick_count: this.tickCount.toString(), staying_second: ((Date.now() - this.mountedTime) / 1000).toFixed(2), }, }); try { (_a = this.lifecycleSagaTask) === null || _a === void 0 ? void 0 : _a.cancel(); } catch (e) { // In rare case, it may throw error, just ignore } (_b = this.unsubscribeFocus) === null || _b === void 0 ? void 0 : _b.call(this); (_c = this.unsubscribeBlur) === null || _c === void 0 ? void 0 : _c.call(this); AppState.removeEventListener("change", this.onAppStateChange); } render() { return <ComponentType {...this.props}/>; } *lifecycleSaga() { var _a; /** * CAVEAT: * Do not use <yield* executeAction> for lifecycle actions. * It will lead to cancellation issue, which cannot stop the lifecycleSaga as expected. * * https://github.com/redux-saga/redux-saga/issues/1986 */ const props = this.props; const enterActionName = `${moduleName}/@@ENTER`; const startTime = Date.now(); if ("navigation" in props) { yield rawCall(executeAction, enterActionName, lifecycleListener.onEnter.bind(lifecycleListener), ((_a = props.route) === null || _a === void 0 ? void 0 : _a.params) || {}); } else { yield rawCall(executeAction, enterActionName, lifecycleListener.onEnter.bind(lifecycleListener), {}); } app.logger.info({ action: enterActionName, elapsedTime: Date.now() - startTime, info: { component_props: JSON.stringify(props), }, }); if (this.hasOwnLifecycle("onTick")) { const tickIntervalInMillisecond = (lifecycleListener.onTick.tickInterval || 5) * 1000; const boundTicker = lifecycleListener.onTick.bind(lifecycleListener); const tickActionName = `${moduleName}/@@TICK`; while (true) { yield rawCall(executeAction, tickActionName, boundTicker); this.tickCount++; yield delay(tickIntervalInMillisecond); } } } }, _a.displayName = `ModuleBoundary(${moduleName})`, // Copy static navigation options, important for navigator _a.navigationOptions = ComponentType.navigationOptions, _a; } } //# sourceMappingURL=ModuleProxy.js.map