core-native
Version:
A lightweight framework based on React Native + Redux + Redux Saga, in strict TypeScript.
132 lines • 6.69 kB
JavaScript
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}/@`,
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}/@`;
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}/@`;
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