效果图
典型的tab导航应用
界面路由
使用的是react-native-router-flux(简称rnrf),基于react-navigation,这两者目前还都不稳定
rnrf中主要的概念有:Router、Scene、Actions等,具体内容可以去官网了解
react-redux结合rnrf的使用
redux中的Store只有一个,且应该定义在应用最上层
rnrf中的Router本质是component,是所有界面的顶层视图
那么Store应该从Router开始向下传递
上代码
-
const createStoreWithMiddleware = applyMiddleware(thunk)(createStore)
-
const store = createStoreWithMiddleware(allReducers)
-
const styles = StyleSheet.create({
-
navigationBar: {
-
backgroundColor: yellow_EF7907
-
},
-
title: {
-
color:
‘white’
-
},
-
tabbarContainer: {
-
backgroundColor:
“#f6f6f6”,
-
}
-
})
-
-
//应用中需要使用的页面都在这里声明
-
export
default
class DemoApp extends BasePureComponent {
-
render() {
-
return (
-
<Provider store={store}>
-
<Router createReducer={reducerCreate}>
-
<Scene key=”root”>
-
<Scene
-
key=”tabbar”
-
tabs={true} //使用TabNavigator导航
-
tabBarPosition=”bottom” //tab的位置
-
tabBarStyle={styles.tabbarContainer} //底部tab样式
-
showLabel={false} //是否显示底部tab的文字
-
lazy={true} //每个tab的页面是否懒加载
-
swipeEnabled={true} //是否允许滑动切换tab
-
-
// navigationBarStyle={styles.navigationBar} //顶部标题栏样式
-
// titleStyle={styles.title} //顶部标题样式
-
navBar={NavBar}
-
>
-
-
<Scene icon={TabIcon} key={SCENE_HOME.key} component={HomeProvider}
-
title={SCENE_HOME.title}/>
-
<Scene icon={TabIcon} key={SCENE_CLASSIFY.key} component={ClassifyPage}
-
title={SCENE_CLASSIFY.title}/>
-
-
<Scene icon={TabIcon} key={SCENE_FOLLOW.key} component={FollowPage}
-
title={SCENE_FOLLOW.title}/>
-
-
<Scene icon={TabIcon} key={SCENE_YUBA.key} component={YubaPage} title={SCENE_YUBA.title}/>
-
<Scene icon={TabIcon} key={SCENE_ME.key} component={MeProvider} title={SCENE_ME.title}/>
-
</Scene>
-
</Scene>
-
</Router>
-
</Provider>
-
)
-
}
-
-
componentDidMount() {
-
printf(‘store’, store)
-
printf(‘state’, store.getState())
-
}
-
}
react-redux使用Provider组件传递store,从Provider源码可以略知一二
-
export
function createProvider() {
-
var _Provider$childContex;
-
-
var storeKey =
arguments.length >
0 &&
arguments[
0] !==
undefined ?
arguments[
0] :
‘store’;
redux应用结构
既然使用了redux,就得按照redux要求的方式写代码,工程目录结构也得顺应潮流
实践示例
下面的步骤都是以首页为例
创建组件
组件化的首页代码十分清爽,如下
-
class HomePage extends BasePureComponent {
-
render() {
-
this.logMsg(
‘render’)
-
this.logMsgWithKey(
‘props’,
this.props)
-
return (
-
<ScrollView style={styles.container}>
-
<BannerProvider/>
-
<ClassifyProvider/>
-
<RecoListProvider/>
-
</ScrollView>
-
)
-
}
-
-
componentDidMount() {
-
let param = {
-
from:
‘home-index’
-
}
-
this.props.refreshHomePage(param)
-
}
-
}
声明组件的action
组件中存在哪些事件,将他们用action表示出来
首页需要一个刷新事件,就按如下定义
export const REFRESH_HOMEPAGE = 'refreshHomePage' //刷新首页
-
const refreshHomePageAction = (data = {}) => {
-
return {
-
type: REFRESH_HOMEPAGE,
-
data
-
}
-
}
-
-
export
const refreshHomePage =
(dispatch, param) => {
-
refreshHomePageTask(dispatch, param, refreshHomePageAction)
-
}
refreshHomePageAction就表示刷新事件的action,这个action携带一个数据data
data有什么用呢?
refreshHomePage这个方法什么时候执行呢?
dispatch这个参数是什么?
param又从哪里来?
refreshHomePageTask又是什么鬼?
好吧,带着这些问题继续往下看
声明action对应的task
从refreshHomePageAction的定义可以看到,我们
并不想这个action立即被dispatch,而是将它交给了refreshHomePageTask
赶紧看看refreshHomePageTask是什么
-
export
const refreshHomePageTask =
(dispatch, param = {}, action) => {
-
printf(
‘refreshHomePageTask’ +
‘_请求参数:’, param)
-
return
new Promise(
(resolve, reject) => {
-
resolve(initHomeData())
-
}).then(
(res) => {
-
printf(
‘refreshHomePageTask’ +
‘_请求结果:’, res)
-
dispatch(action(res))
-
})
-
}
-
initHomeData =
() => {
-
return {
-
status:
200,
-
serverTime:
new
Date().valueOf()
-
}
-
}
refreshHomePageTask接收三个参数
dispatch:分发器(暂时这么理解,可以肯定地告诉你,
它来自store,而且是个函数)
param:请求参数(可以认为是传给task的参数)
action:需要分发的事件(这里实际接收的就是refreshHomePageAction)
在执行完Promise容器中的内容后,将结果交给refreshHomePageAction
再回顾一下refreshHomePageAction的模样,它返回的是一个普通对象(可以理解成final action),task执行的结果最终变成这个普通对象的一部分
-
const refreshHomePageAction =
(data = {}) => {
-
return {
-
type: REFRESH_HOMEPAGE,
-
data
-
}
-
}
最后执行dispatch方法,将这个final action分发出去,接下来就会执行reducer
声明组件的reducer
上代码
-
export
const homeIndex =
(state = initHomeData(), action) => {
-
printf(
‘homeIndex Reducer receive an action:’, action)
-
switch (action.type) {
-
case REFRESH_HOMEPAGE:
-
return action.data
-
default:
-
return state
-
}
-
}
-
initHomeData =
() => {
-
return {
-
status:
200,
-
serverTime:
new
Date().valueOf()
-
}
-
}
homeIndex方法接收state和action两个参数,state可以有初始值
redux是怎么会执行homeIndex这个方法的呢?
还记得在界面路由创建的store时传入的reducer吗?再贴一遍代码。
-
const createStoreWithMiddleware = applyMiddleware(thunk)(createStore)
-
const store = createStoreWithMiddleware(allReducers)
因为reducer按应用模块划分,所以每个模块的reducer都放在了allReducers中,看到homeIndex了吗?
-
//所有的reducer
-
export
const allReducers = combineReducers({
-
homeIndex,
-
homeBanner,
-
homeClassify,
-
homeRecolist,
-
meIndex
-
});
正是因为创建store时,reducer是作为参数传入的,那么redux在dispatch一个action之后,redux当然知道要执行哪些reducer了(事实上传入的每个reducer都会被执行,最后会通过log验证这个事实)
绑定组件
主要就是mapXXXToProps那两个方法
上代码
-
//state中包括了所有绑定组件的state,return的是当前组件需要放在props中的数据
-
mapStateToProps =
(state) => {
-
return state.homeIndex
-
}
-
-
//return 的是组件中需要放在props中的方法
-
mapDispatchToProps =
(dispatch) => {
-
return {
-
refreshHomePage:
(param) => refreshHomePage(dispatch, param)
-
}
-
}
-
-
//绑定组件
-
export
default connect(
-
mapStateToProps,
-
mapDispatchToProps
-
)(HomePage)
比如 state.homeIndex被某个reducer改成如下
-
{
-
status:
200,
-
serverTime:
1314
-
}
组件的props被更新后,就可以通过 this.props.status和this.props.serverTime得到这些值
mapDispatchToProps中返回的是一个对象,这个对象中每个属性的值是一个方法,属性名就是组件中使用this.props.XXX()调用的那个XXX,最终这些属性也会变成组件props的一部分
log验证
苍白的文字描述得再多,不如用铁打的事实来得直接
我们在可能有疑问的地方加上log
这里
-
if (firstRender){
-
this.logMsgWithKey(‘初始props’, this.props)
-
}
else{
-
this.logMsgWithKey(‘新的props’, this.props)
-
}
-
firstRender =
false
这里
-
export
const refreshHomePageTask =
(dispatch, param = {}, action) => {
-
printf(‘refreshHomePageTask’ + ‘_请求参数:’, param)
-
return
new
Promise(
(resolve, reject) => {
-
resolve(initHomeData())
-
}).then(
(res) => {
-
printf(‘refreshHomePageTask’ + ‘_请求结果:’, res)
-
dispatch(action(res))
-
})
-
}
还有这里
-
export
const homeIndex =
(state = initHomeData(), action) => {
-
printf(‘homeIndex Reducer receive an action:’, action)
-
switch (action.type) {
-
case REFRESH_HOMEPAGE:
-
return action.data
-
default:
-
return state
-
}
-
}
执行后得到如下铁证