0%

为react-native dva 封装request

之前写的web网站一直用的react+dva框架做数据状态管理,觉得很便捷。写react native移动应用也尝试用这框架来写,但是看了原作者的意思,是没有打算自己再专门写个脚手架。查看issues发现了react-native-dva-starter 这个封装的架子,但是其中没有向Web那样封装好request,这里记录下自己封装的记录。

环境:

  1. “react-native”: “^0.55.4”
  2. “dva-core”: “^1.3.0”
  3. “react-navigation”: “^2.5.1”
  4. “react-redux”: “^5.0.7”

步骤:

1. 新建文件app\utils\request.js

1
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
94
95
96
97
98
99
/* eslint-disable prefer-template */
import {Loading} from '../components/NetworkLoading'// 自定义loading组件
import Toast from 'react-native-root-toast'; // 引入提示信息类库

const codeMessage = {
200: '服务器成功返回请求的数据。',
201: '新建或修改数据成功。',
202: '一个请求已经进入后台排队(异步任务)。',
204: '删除数据成功。',
400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
401: '用户没有权限(令牌、用户名、密码错误)。',
403: '用户得到授权,但是访问是被禁止的。',
404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
406: '请求的格式不可得。',
410: '请求的资源被永久删除,且不会再得到的。',
422: '当创建一个对象时,发生一个验证错误。',
500: '服务器发生错误,请检查服务器。',
502: '网关错误。',
503: '服务不可用,服务器暂时过载或维护。',
504: '网关超时。',
};
function checkStatus(response) {
Loading.hidden()
if (response.status >= 200 && response.status < 300) {
return response;
}
const errortext = codeMessage[response.status] || response.statusText;
Toast.show(errortext,{duration: Toast.durations.LONG,position: Toast.positions.CENTER,})
const error = new Error(errortext);
error.name = response.status;
error.response = response;
throw error;
}

export function toParams(param) {
var result = ""
for (let name in param) {
if (typeof param[name] != 'function') {
result += "&" + name + "=" + encodeURI(param[name]);
}
}
return result.substring(1)
}

/**
* 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) {
Loading.show()
const defaultOptions = {
credentials: 'include',
};
const newOptions = { ...defaultOptions, ...options };
if (newOptions.method === 'POST' || newOptions.method === 'PUT') {
if (!(newOptions.body instanceof FormData)) {
newOptions.headers = {
Accept: 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
// 'Content-Type': 'application/json;charset=UTF-8',
...newOptions.headers,
};
// newOptions.body = JSON.stringify(newOptions.body);
newOptions.body = toParams(newOptions.body);
} else {
// newOptions.body is FormData
newOptions.headers = {
Accept: 'application/json',
...newOptions.headers,
};
}
}
console.log(newOptions)

//超时处理
let timer = setTimeout(() => {
Loading.hidden()
Toast.show('请求超时,请烧后重试!',{duration: Toast.durations.SHORT,position: Toast.positions.CENTER,})
}, 30000);

return fetch('http://app.xxx.cn' + url, newOptions)
.then(checkStatus)
.then(response => {
timer && clearTimeout(timer);
if (newOptions.method === 'DELETE' || response.status === 204) {
return response.text();
}
return response.json();
})
.catch(e => {
console.log("e:",e)
Loading.hidden()
//异常处理,根据需要完善

});
}

这里参考了antd-pro里封装的request。需要注意的是请求的headers和body数据格式,要符合接口定义,注释掉的部分是json格式的处理,因为我的接口用的x-www-form-urlencoded,需要处理下。这里的超时处理并没有终止掉接口请求,只是终止了loading。

2. 新建api池,新建文件app\services\api.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { stringify } from 'qs';
import request from '../utils/request';

export async function queryPostiList(params) {
return request('/api/postlist', {
method: 'POST',
body: params,
});
}
export async function queryGetList(params) {
return request(`/api/getlist?${stringify(params)}`);
}
export async function queryGetInfo(params) {
return request(`/api/getGetInfo/${params.id.toString()}?_t=${params.time.toString()}`);
}

这几种请求基本就涵盖大部分功能了,上传文件的没写这个里。

3. 接下就是写models掉api了,在对应models调用处理返回数据即可。

例如:app\models\app.js

1
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
import * as apiService from '../services/api'
import {createAction} from "../utils";
import Toast from 'react-native-root-toast'; // 引入类库

export default {
namespace: 'app',
state: {
List: [],
fetching:false
},
reducers: {
saveList(state, {payload}) {
return {
...state,
List: payload.resultCode,
};
},
updateState(state, {payload}) {
return {...state, ...payload}
},
},
effects: {
*queryList({payload}, {call, put}) {
yield put(createAction('updateState')({fetching: true}))
const queryList = yield call(apiService.queryPostiList, payload)
if (queryList) {
// yield put(NavigationActions.back())
yield put({
type: 'saveList',
payload: queryList,
});
}
yield put(createAction('updateState')({fetching: false}))
},
},
subscriptions: {
setup({dispatch}) {
//dispatch({type: 'loadStorage'})前置处理
},
},
}

4. 调用models方法发请求

在对应的页面方法里调用即可,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
...
@connect(({ app }) => ({ ...app }))
class Gongyi extends Component {
state={
Data:[],
}
componentWillMount() {
this.props.dispatch({
type:'app/queryList',
payload: {
"ccserm":"F3C1CDFBF309DA9C3BCKF2345",
"lat":location.coords.latitude,
"lon":location.coords.longitude,
"p":pagNum,
"place":"0",
"time":"2"},
})
.then(()=>{
const { List } = this.props
this.setState({
Data : List,
},()=>{console.log(this.state.Data)})
})
}
...

实际请求时数据流是按照:4>3>2>1>2>3>4 ,这样去走的。