项目demo :https://github.com/shifeng1993/rndemo2
rn版本: 0.48.3
本节内容
- 状态管理(redux)
- 请求库封装 (axios)
未完成内容
- 公共样式管理,与兼容 (ios&android)
- 兼容性基础组件的封装 (baseView)
注:由于之前写过demo,所以有个排坑指南,
https://www.shifeng1993.com/2017/09/07/reactnative_bug/ 问题和bug可以去里面找找
架构搭建
因为请求库属于工具的一部分,而状态管理会用到请求库,所以先封装请求库。
请求库封装 (axios)
注:封装请求库尽量根据实际项目情况来。例如查询,批量修改,批量删除,等非常规操作。
添加axios 和querystring依赖
yarn add axios && yarn add querystring
新建 src/common/httpBaseConfig.js
1 2 3 4 5
| export default httpBaseConfig = { baseUrl: 'http://192.168.212.60', port: '3000', prefix: '' }
|
新建 src/utils/http/index.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
| import axios from 'axios'; import qs from 'querystring'; import baseConfig from '../../common/httpBaseConfig.js'; axios.defaults.baseURL = baseConfig.baseUrl + ':' + baseConfig.port + baseConfig.prefix;
export default class http { static async get(url, params) {
try { let query = await qs.stringify(params) let res = null; if (!params) { res = await axios.get(url) } else { res = await axios.get(url + '?' + query) } return res } catch (error) { return error } } static async post(url, params) { try { let res = await axios.post(url, params) return res } catch (error) { return error } } static async patch(url, params) { try { let res = await axios.patch(url, params) return res } catch (error) { return error } } static async put(url, params) { try { let res = await axios.put(url, params) return res } catch (error) { return error } } static async delete(url, params) {
try { let res = await axios.post(url, params) return res } catch (error) { return error } } }
|
引入到入口文件并注册为全局 app.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import React, { Component } from 'react'; import { StyleSheet, Text, View } from 'react-native'; import Icon from 'react-native-vector-icons/Ionicons'; import AppNavigator from './src/router';
import http from './src/utils/http' global.http = http
export default class App extends Component { render() { return ( <AppNavigator></AppNavigator> ); } }
|
在Home页面render函数中打印全局http,如果不为未定义,则引入成功
状态管理(redux)
react-native集成redux,0.44以后得带上react-navigation路由参数;
然而路由参数不能像B端的react-router-redux一样同步。
所以必须得手写一个函数进行同步,还好官方出了这个,虽然有些麻烦,但是还是可以搞的。
首先,安装redux依赖
yarn add redux && yarn add react-redux && yarn add redux-thunk
添加store
新建 src/store/index.js
存放store
1 2 3 4 5 6 7 8 9 10 11 12 13
| import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk';
import getReducers from './reducers';
export default function getStore(navReducer) { return createStore( getReducers(navReducer), undefined, applyMiddleware(thunk) ); }
|
添加初始state
新建 src/store/states/goods/index.js
1 2 3 4 5 6 7
| export default { goodsList: { data:[] }, goods: {} };
|
添加reducer
新建 src/store/reducers/index.js
合并单个类的reducer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import { combineReducers } from 'redux';
import goods from './goods'
const reducers = { goods };
export default function getReducers(navReducer) { return combineReducers({ ...reducers, nav: navReducer }); }
|
新建商品类的reducer src/store/reducers/goods/index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import * as types from '../../constants/actionTypes'; import goodsState from '../../states/goods';
export default function goods(state = goodsState, action) { switch (action.type) { case types.SET_GOODSLIST: return { ...state, goodsList: action.data }; case types.SET_GOODS: return { ...state, goods: action.data }; default: return state; } }
|
添加actions
添加actionTypes
新建action类型 src/store/constants/actionTypes.js
1 2 3 4 5 6 7
|
export const SET_GOODSLIST = 'SET_GOODSLIST';
export const SET_GOODS = 'SET_GOODS';
|
添加action
新建商品类动作 src/store/actions/goods/index.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
| import * as types from '../../constants/actionTypes';
const PAGE_SIZE = 10
const fetchGoodsList = (currentPage) => { let params = {} params.currentPage = currentPage; params.pageSize = PAGE_SIZE; return http.get('/goods/getGoodsList', params) }
export const getGoodsList = (currentPage) => { return (dispatch, getState) => { return fetchGoodsList(currentPage).then((res) => { if (res.status === 200) { let goodsList = {} if (currentPage === 1) { goodsList = res.data } else { goodsList.data = getState().goods.goodsList.data.concat(res.data.data) goodsList.pageTotal = parseInt(res.data.pageTotal) goodsList.currentPage = parseInt(res.data.currentPage) goodsList.pageSize = parseInt(res.data.pageSize) } dispatch(setGoodsList(goodsList)); } else { alert(res) } }); }; };
export const setGoodsList = (data) => { return {type: types.SET_GOODSLIST, data}; };
|
将store加载到主入口处
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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| import React, {Component} from "react"; import {Provider, connect} from "react-redux"; import {AsyncStorage, BackHandler} from "react-native"; import {NavigationActions, addNavigationHelpers} from "react-navigation"; import Ionicons from 'react-native-vector-icons/Ionicons'; import Faicon from 'react-native-vector-icons/FontAwesome';
import getStore from "./src/store"; import AppNavigator from './src/router'; import http from './src/utils/http'
global.http = http global.storage = AsyncStorage; global.Ionicon = Ionicons global.Faicon = Faicon
const navReducer = (state, action) => { const newState = AppNavigator .router .getStateForAction(action, state); return newState || state; };
const mapStateToProps = (state) => ({nav: state.nav});
class App extends Component { componentDidMount() { BackHandler.addEventListener("hardwareBackPress", this.onBackPress); } componentWillUnmount() { BackHandler.removeEventListener("hardwareBackPress", this.onBackPress); } onBackPress = () => { const {dispatch, nav} = this.props; if (nav.index === 0) { return false; } dispatch(NavigationActions.back()); return true; }; render() { console.log(this.props.nav) const {dispatch, nav} = this.props; const navigation = addNavigationHelpers({dispatch, state: nav}); return (<AppNavigator navigation={navigation}/>); } }
const AppWithNavigationState = connect(mapStateToProps)(App);
const store = getStore(navReducer);
export default function Root() { return ( <Provider store={store}> <AppWithNavigationState/> </Provider> ); }
|
测试store
打开js debugging,进行tabbar切换,如果有路由信息打印出来,redux则添加成功
注: console.log 在app.js 的第46行,不需要则删除
测试action
注:如按上述写那么还需要启动mongo-server服务。
文档: https://www.shifeng1993.com/2017/08/21/rn_react_koa2_mongodb_api1/
项目: https://github.com/shifeng1993/mongo-server
修改src/containers/Home/index.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
| import React, {Component} from 'react'; import {StyleSheet, Text, View} from 'react-native'; import {bindActionCreators} from 'redux' import {connect} from 'react-redux'
import * as goodsAction from '../../store/actions/goods';
const INITIAL_PAGE = 1;
class Home extends Component { constructor(props) { super(props); } componentWillMount() { const {actions} = this.props; actions.getGoodsList(INITIAL_PAGE) } render() { console.log(this.props.goodsList.data) return ( <View> <Text>主页</Text> </View> ); } }
const mapStateToProps = state => { return {goodsList: state.goods.goodsList}; }
const mapDispatchToProps = (dispatch) => ({ actions: bindActionCreators(goodsAction, dispatch), dispatch: dispatch });
export default connect(mapStateToProps, mapDispatchToProps)(Home)
|
刷新应用,这时如果有console.log打印出来空数组或者10个商品的数组,则代表action添加成功。
如果mongo-server没有添加商品也是为空数组。