1.项目文件启动顺序
(1.)/index.js
import { StatusBar, Platform, View } from 'react-native';
import { Provider } from 'react-redux';
import Config from 'react-native-config';
import { PersistGate } from 'redux-persist/integration/react';
import { store, persistor } from '~/modules/redux-app-config';
import * as WeChat from 'react-native-wechat';
import AppContent from './AppContent';
import codePush from 'react-native-code-push';
import NetInfoManager from '~/modules/services/netInfo'; // 引入网络状态变化
class App extends Component {
UNSAFE_componentWillMount() {
codePush.disallowRestart(); // 设置热更不允许重启
}
componentDidMount() {
console.log('============= Config ============= ', Config);
WeChat.registerApp(Config.WECHAT_ID); // 注册微信AppID
if (Platform.OS === 'android') {
StatusBar.setTranslucent(true);
StatusBar.setBackgroundColor('transparent');
StatusBar.setBarStyle('dark-content');
}
}
render() {
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}> // 设置redux缓存
<AppContent /> // 加载自定义的
</PersistGate>
</Provider>
);
}
}
App = codePush({ installMode: codePush.InstallMode.ON_NEXT_RESTART })(App); // 设置热更新下一次重启生效
export default App;
(2)/AppContent.js
import React, { Component } from 'react';
import {
View,
StyleSheet,
Platform,
BackHandler,
ToastAndroid,
StatusBar,
PermissionsAndroid,
} from 'react-native';
import Promise from 'bluebird';
import UI from '~/modules/UI';
import { store, dispatch } from '~/modules/redux-app-config';
import i18n from '~/i18n';
import { getRouteName, checkPermission } from '~/modules/services/utils';
import AppWithNavigationState from './AppRouter';
import AppLoading from './AppLoading';
import AppEnv from './AppEnvModal';
import ContentLoader from '~/components/ContentLoader';
import EventTracking from '~/modules/services/event-tracking';
class AppContent extends Component {
UNSAFE_componentWillMount() {
if (Platform.OS === 'android') {
BackHandler.addEventListener('hardwareBackPress', this.onBackAndroid.bind(this));
}
this.permissionsTrack(); // 权限埋点
// if (!store.getState().userInfo.user) {
// asyncAction('USER_LOGIN');
// }
}
componentWillUnmount() {
if (Platform.OS === 'android') {
BackHandler.removeEventListener('hardwareBackPress', this.onBackAndroid.bind(this)); //安卓点击两次返回退出app
}
}
async permissionsTrack() {
await Promise.delay(10 * 1000);
const storagePer = await checkPermission(PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE);
const readPhonePer = await checkPermission(PermissionsAndroid.PERMISSIONS.READ_PHONE_STATE);
const { readStorage, readPhoneState } = store.getState().permissionsInfo;
if (!readStorage && storagePer) {
EventTracking.track('t0101', 'r0130');
EventTracking.track('t0105', 'r0130');
dispatch('SET_PERMISSIONS_INFO', { readStorage: true });
}
if (!readPhoneState && readPhonePer) {
EventTracking.track('t0101', 'r0131');
EventTracking.track('t0105', 'r0131');
dispatch('SET_PERMISSIONS_INFO', { readPhoneState: true });
}
}
onBackAndroid() {
// return false;
const routeName = getRouteName(store.getState().nav);
if (routeName === 'grade') {
return true;
}
if (routeName !== 'lesson' && routeName !== 'pageRegister') {
store.dispatch({ type: 'Navigation/BACK' });
return true;
}
if (this.lastBackPressed && this.lastBackPressed + 2000 >= Date.now()) {
// 最近2秒内按过back键,可以退出应用。
return false;
}
this.lastBackPressed = Date.now();
ToastAndroid.show('再按一次退出程序', ToastAndroid.SHORT);
return true;
}
render() {
return (
<View style={styles.container}>
<View style={{ flex: 1 }}>
<AppWithNavigationState />
</View>
<AppLoading /> // 加载loading
<AppEnv /> // 加载设置环境modal
<ContentLoader />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
export default AppContent;
(3)/.env.local .env.development .env.production 各个开发环境设置
(4)/js/modules/services/request.js 网络请求
import { Alert } from 'react-native';
import { dispatch, store } from '~/modules/redux-app-config';
import { Toast } from 'teaset';
import Global from '~/modules/global';
function checkStatus(response) {
console.log(' ============ response ============ ', response);
if (response.code === 401) {
dispatch('GET_USERINFO'); // 如果网络请求返回的的code为401, 那么获取用户信息,重定向到登陆页面
}
return response;
}
function checkNet() {
return store.getState().netInfoStatus.status;
}
/**
* Requests a URL, returning a promise.
*
* @param {string} url The URL we want to request
* @param {object} [options] The options we want to pass to "fetch"
* @return {object} An object containing either "data" or "err"
*/
export default function request(url, options) {
const status = checkNet();
if (!status) {
return new Promise(res => {
Toast.fail('请检查网络');
res({ msg: 'fail' });
});
}
if (!url.startsWith('http')) {
url = Global.getApiUrl() + url; // 设置请求url
}
const defaultOptions = {};
const newOptions = { ...defaultOptions, ...options };
// 设置header请求头, 这里主要是加了token的header
newOptions.headers = {
Connection: 'close',
type: 'getUserData',
'Content-Type': 'application/json; charset=utf-8',
...newOptions.headers,
};
console.log(' ============ request ============ ', { url, newOptions });
return Promise.race([
fetch(url, newOptions),
new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('Request Timeout')); // 设置超时10秒
}, 10000);
}),
])
.then(response => response.json())
.then(checkStatus) // 检查网络
.catch(e => {
if (e.toString() === 'Error: Request Timeout') {
Toast.fail('请求超时'); // 提示网络请求超时
}
});
}