# React Native进阶
# 一、App导航框架设计
./src/index.js,作用是创建AppContainer并导出
import React from 'react';
import { createAppContainer, createSwitchNavigator } from 'react-navigation';
import { createBottomTabNavigator } from 'react-navigation-tabs';
import { createStackNavigator } from 'react-navigation-stack';
import Icon from 'react-native-vector-icons/FontAwesome';
import WelcomePage from './Welcome/index'; // 欢迎页
import HomeStackNavigator from './Home/index';
import CompanyStackNavigator from './Company/index';
import Message from './Message';
import My from './My';
// 底部tab导航,应用主体
const TabNavigator = createBottomTabNavigator(
{
Home: {
screen: HomeStackNavigator,
navigationOptions: {
tabBarLabel: '职位',
}
},
Company: {
screen: CompanyStackNavigator,
navigationOptions: {
tabBarLabel: '公司',
}
},
Message: {
screen: Message,
navigationOptions: {
tabBarLabel: '消息',
}
},
My: {
screen: My,
navigationOptions: {
tabBarLabel: '我的',
}
}
},
{
initialRouteName: 'Home',
defaultNavigationOptions: ({ navigation }) => ({
tabBarIcon: ({ focused, horizontal, tintColor }) => {
const { routeName } = navigation.state;
let iconName;
if (routeName === 'Home') {
iconName = 'globe';
} else if (routeName === 'Company') {
iconName = 'building-o';
} else if (routeName === 'Message') {
iconName = 'comments-o';
} else if (routeName === 'My') {
iconName = 'user-circle-o';
}
return <Icon name={iconName} size={20} color={tintColor} />;
},
}),
tabBarOptions: {
activeTintColor: 'rgb(29,216,200)',
inactiveTintColor: 'gray',
},
},
);
// app欢迎页面,因为要切换到底部tab导航,所以要创建一个createStackNavigator
const AppInitNavigator = createStackNavigator({
welcome: {
screen: WelcomePage,
navigationOptions: {
header: null,
}
}
});
// 因为需要从欢迎页面切换到底部tab导航并且只有1次,所以用createSwitchNavigator
const switchNavigator = createSwitchNavigator({
Init: AppInitNavigator,
Main: TabNavigator,
}, {
initialRouteName: 'Init',
});
const AppContainer = createAppContainer(switchNavigator);
export default AppContainer;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# 二、Redux与React Navigation结合集成
App.js
import React, { Component } from 'react';
import { StyleSheet, StatusBar } from 'react-native';
import {
createReduxContainer,
createReactNavigationReduxMiddleware
} from 'react-navigation-redux-helpers';
import { createStore, applyMiddleware } from 'redux';
import { Provider, connect } from 'react-redux';
import AppContainter from './src/index';
import appReducer from './src/reducer';
const middleware = createReactNavigationReduxMiddleware(state => state.nav);
const AppReduxContainer = createReduxContainer(AppContainter);
const mapStateToProps = state => ({
state: state.nav,
});
const AppWithNavigationState = connect(mapStateToProps)(AppReduxContainer);
const store = createStore(appReducer, applyMiddleware(middleware));
class App extends Component {
render() {
return (
<Provider store={store} style={styles.container}>
<StatusBar barStyle="light-content" />
<AppWithNavigationState />
</Provider>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
export default App;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
./src/reducer.js,作用是combineReducers,把reducer结合起来
import { combineReducers } from 'redux';
import { createNavigationReducer } from 'react-navigation-redux-helpers';
import AppContainter from './index';
import companyList from './Company/reducer';
const navReducer = createNavigationReducer(AppContainter);
export default combineReducers({
nav: navReducer,
companyList
});
2
3
4
5
6
7
8
9
10
11
12
# 三、网络编程
React Native 提供了和 web 标准⼀致的Fetch API,⽤于满⾜开发者访问⽹络的需求。
fetch('https://mywebsite.com/mydata.json');
// 提交数据
fetch('https://mywebsite.com/endpoint/', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
firstParam: 'yourValue',
secondParam: 'yourOtherValue',
}),
});
// 普通表单请求
fetch('https://mywebsite.com/endpoint/', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'key1=value1&key2=value2',
});
// 返回promise
function getMoviesFromApiAsync() {
return fetch('https://facebook.github.io/react-native/movies.json')
.then((response) => response.json())
.then((responseJson) => {
return responseJson.movies;
})
.catch((error) => {
console.error(error);
});
}
// async/await语法
async function getMoviesFromApi() {
try {
// 注意这;里的await语句,其所在的函数必须有async关键字声明
let response = await fetch(
'https://facebook.github.io/react-native/movies.json',
);
let responseJson = await response.json();
return responseJson.movies;
} catch (error) {
console.error(error);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# 错误处理
当接收到⼀个代表错误的HTTP状态码时,从fetch()返回的promise不会被标记为reject,即使该HTTP响应的状态码是404或500。相反,它会将Promise状态标记为resolve(但是会将resolve的返回值的ok属性设置为false),仅当⽹络故障时或请求被阻⽌时,才会被标记为reject。⼀次请求没有调⽤reject并不代表请求⼀定成功了,通常需要在resolve情况下,再判断response.ok属性为true.
let url = `https://api.github.com/search/repositories?q=NodeJS`;
fetch(url)
.then(response => {
if (response.ok) {
return response.text();
}
throw new Error("Network response was not ok");
})
.then(responseText => {
console.log(responseText);
})
.catch(e => {
console.log(e.toString());
});
2
3
4
5
6
7
8
9
10
11
12
13
14
# 四、数据存储AsyncStorage
AsyncStorage 是⼀个简单的、异步的、持久化的 Key-Value 存储系统,它对于 App 来说是全局性的。可⽤来代替 LocalStorage。推荐在AsyncStorage的基础上做⼀层抽象封装,⽽不是直接使⽤ AsyncStorage。
在iOS上,AsyncStorage 在原⽣端的实现是把较⼩值存放在序列化的字典中,而把较大值写⼊单独的⽂件。在Android上,AsyncStorage 会尝试使⽤RocksDB,或选择 SQLite。
安装: yarn add @react-native-community/async-storage
# 基本使用
import AsyncStorage from '@react-native-community/async-storage';
// 存数据
async doSave(){
//⽤法1
AsyncStorage.setItem(Key, Value, err => {
err && console.log(err.toString())
})
//⽤法2
AsyncStorage.setItem(Key, Value)
.catch(e => {
err && console.log(err.toString())
})
//⽤法3
try {
await AsyncStorage.setItem(Key, value)
} catch (err) {
err && console.log(err.toString())
}
}
// 读取数据
async getData(){
//⽤法1
AsyncStorage.getItem(Key, (err, value) => {
console.log(value)
err && console.log(err.toString())
})
//⽤法2
AsyncStorage.getItem(Key)
.then(value => {
console.log(value)
})
.catch(e => {
err && console.log(err.toString())
})
//⽤法3
try {
const value = await AsyncStorage.getItem(Key)
console.log(value)
} catch (err) {
err && console.log(err.toString())
}
}
// 删除数据
async doRemove(){
//⽤法1
AsyncStorage.removeItem(Key, (err) => {
err && console.log(err.toString())
})
//⽤法2
AsyncStorage.removeItem(Key)
.catch(e => {
err && console.log(err.toString())
})
//⽤法3
try {
await AsyncStorage.removeItem(Key)
} catch (err) {
err && console.log(err.toString())
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# 五、离线缓存框架设计
离线缓存有什么好处?
- 提升用户体验,⽤户的⽹络情况我们不能控制,但是我们可以离线存储提升体验。
- 节省流量:节省服务器流量,节省⽤户⼿机的流量
离线缓存有什么限制?数据的实时性要求不高,推荐使用
离线缓存的策略:
- 优先从本地获取数据,如果数据过时或者不存在,则从服务器获取数据,数据返回后同时将数据同步到本地数据库。
- 优先从服务器获取数据,数据返回后同步到本地数据库,如果发⽣⽹络故障,才从本地获取数据。
离线缓存框架的设计:按照第⼀个策略,如果数据过时或者不存在,则从服务器获取数据,数据返回后同时将数据同步到本地数据库。
- 优先从本地获取数据
- 如果数据存在且在有效期内,我们将数据返回
- 否则获取⽹络数据
封装DataStore,外部调用使用fetchData方法即可
import AsyncStorage from "@react-native-community/async-storage";
export default class DataStore {
// 检查有效期
static checkTimestampValid(timestamp) {
const currentDate = new Date();
const targetDate = new Date();
targetDate.setTime(timestamp);
if (currentDate.getMonth() !== targetDate.getMonth()) return false;
if (currentDate.getDate() !== targetDate.getDate()) return false;
if (currentDate.getHours() - targetDate.getHours() > 4) return false; //有效期4个⼩时
return true;
}
// 外部调用
fetchData(url) {
return new Promise((resolve, reject) => {
//获取本地数据
this.fetchLocalData(url)
.then(wrapdata => {
//检查有效期
if (wrapdata && DataStore.checkTimestampValid(wrapdata.timestamp)) {
resolve(wrapdata);
} else {
//获取⽹络数据
this.fetchNetData(url)
.then(data => {
//给数据打个时间戳
resolve(this._wrapData(data));
})
.catch(e => {
reject(e);
});
}
})
.catch(error => {
this.fetchNetData(url)
.then(data => {
resolve(this._wrapData(data));
})
.catch(error => {
reject(error);
});
});
});
}
// 保存数据到本地
saveData(url, data, callback) {
if (!data || !url) return;
AsyncStorage.setItem(url, JSON.stringify(this._wrapData(data)), callback);
}
// 给离线的数据添加⼀个时间戳,便于计算有效期
_wrapData(data) {
return { data: data, timestamp: new Date().getTime() }; //本地时间,推荐服务器时间
}
// 获取本地数据
fetchLocalData(url) {
return new Promise((resolve, reject) => {
AsyncStorage.getItem(url, (err, result) => {
if (!err) {
resolve(JSON.parse(result)); // getItem获取到的是string,我们需要将其反序列化为object
} else {
reject(err);
console.log(err);
}
});
});
}
// 获取网络数据
fetchNetData(url) {
return new Promise((resolve, reject) => {
fetch(url)
.then(response => {
if (response.ok) {
return response.json();
}
throw new Error("network response was not ok");
})
.then(responseData => {
// 保存数据到本地
this.saveData(url, responseData);
resolve(responseData);
})
.catch(e => {
reject(e);
});
});
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93