Router-flux 结合 redux 使用

效果图
典型的tab导航应用

界面路由
使用的是react-native-router-flux(简称rnrf),基于react-navigation,这两者目前还都不稳定

rnrf中主要的概念有:Router、Scene、Actions等,具体内容可以去官网了解

react-redux结合rnrf的使用
redux中的Store只有一个,且应该定义在应用最上层

rnrf中的Router本质是component,是所有界面的顶层视图

那么Store应该从Router开始向下传递

上代码


    
    
  1. const createStoreWithMiddleware = applyMiddleware(thunk)(createStore)
  2. const store = createStoreWithMiddleware(allReducers)
  3. const styles = StyleSheet.create({
  4. navigationBar: {
  5. backgroundColor: yellow_EF7907
  6. },
  7. title: {
  8. color: ‘white’
  9. },
  10. tabbarContainer: {
  11. backgroundColor: “#f6f6f6”,
  12. }
  13. })
  14. //应用中需要使用的页面都在这里声明
  15. export default class DemoApp extends BasePureComponent {
  16. render() {
  17. return (
  18. <Provider store={store}>
  19. <Router createReducer={reducerCreate}>
  20. <Scene key=”root”>
  21. <Scene
  22. key=”tabbar”
  23. tabs={true} //使用TabNavigator导航
  24. tabBarPosition=”bottom” //tab的位置
  25. tabBarStyle={styles.tabbarContainer} //底部tab样式
  26. showLabel={false} //是否显示底部tab的文字
  27. lazy={true} //每个tab的页面是否懒加载
  28. swipeEnabled={true} //是否允许滑动切换tab
  29. // navigationBarStyle={styles.navigationBar} //顶部标题栏样式
  30. // titleStyle={styles.title} //顶部标题样式
  31. navBar={NavBar}
  32. >
  33. <Scene icon={TabIcon} key={SCENE_HOME.key} component={HomeProvider}
  34. title={SCENE_HOME.title}/>
  35. <Scene icon={TabIcon} key={SCENE_CLASSIFY.key} component={ClassifyPage}
  36. title={SCENE_CLASSIFY.title}/>
  37. <Scene icon={TabIcon} key={SCENE_FOLLOW.key} component={FollowPage}
  38. title={SCENE_FOLLOW.title}/>
  39. <Scene icon={TabIcon} key={SCENE_YUBA.key} component={YubaPage} title={SCENE_YUBA.title}/>
  40. <Scene icon={TabIcon} key={SCENE_ME.key} component={MeProvider} title={SCENE_ME.title}/>
  41. </Scene>
  42. </Scene>
  43. </Router>
  44. </Provider>
  45. )
  46. }
  47. componentDidMount() {
  48. printf(‘store’, store)
  49. printf(‘state’, store.getState())
  50. }
  51. }
react-redux使用Provider组件传递store,从Provider源码可以略知一二


    
    
  1. export function createProvider() {
  2. var _Provider$childContex;
  3. var storeKey = arguments.length > 0 && arguments[ 0] !== undefined ? arguments[ 0] : ‘store’;
redux应用结构
既然使用了redux,就得按照redux要求的方式写代码,工程目录结构也得顺应潮流



实践示例
下面的步骤都是以首页为例

创建组件
组件化的首页代码十分清爽,如下


    
    
  1. class HomePage extends BasePureComponent {
  2. render() {
  3. this.logMsg( ‘render’)
  4. this.logMsgWithKey( ‘props’, this.props)
  5. return (
  6. <ScrollView style={styles.container}>
  7. <BannerProvider/>
  8. <ClassifyProvider/>
  9. <RecoListProvider/>
  10. </ScrollView>
  11. )
  12. }
  13. componentDidMount() {
  14. let param = {
  15. from: ‘home-index’
  16. }
  17. this.props.refreshHomePage(param)
  18. }
  19. }
声明组件的action
组件中存在哪些事件,将他们用action表示出来

首页需要一个刷新事件,就按如下定义

export const REFRESH_HOMEPAGE = 'refreshHomePage' //刷新首页
     
     

     
     
  1. const refreshHomePageAction = (data = {}) => {
  2. return {
  3. type: REFRESH_HOMEPAGE,
  4. data
  5. }
  6. }
  7. export const refreshHomePage = (dispatch, param) => {
  8. refreshHomePageTask(dispatch, param, refreshHomePageAction)
  9. }
refreshHomePageAction就表示刷新事件的action,这个action携带一个数据data

data有什么用呢?

refreshHomePage这个方法什么时候执行呢?

dispatch这个参数是什么? param又从哪里来?

refreshHomePageTask又是什么鬼?

好吧,带着这些问题继续往下看

声明action对应的task
从refreshHomePageAction的定义可以看到,我们 并不想这个action立即被dispatch,而是将它交给了refreshHomePageTask

赶紧看看refreshHomePageTask是什么


     
     
  1. export const refreshHomePageTask = (dispatch, param = {}, action) => {
  2. printf( ‘refreshHomePageTask’ + ‘_请求参数:’, param)
  3. return new Promise( (resolve, reject) => {
  4. resolve(initHomeData())
  5. }).then( (res) => {
  6. printf( ‘refreshHomePageTask’ + ‘_请求结果:’, res)
  7. dispatch(action(res))
  8. })
  9. }

     
     
  1. initHomeData = () => {
  2. return {
  3. status: 200,
  4. serverTime: new Date().valueOf()
  5. }
  6. }
refreshHomePageTask接收三个参数

dispatch:分发器(暂时这么理解,可以肯定地告诉你, 它来自store,而且是个函数)
param:请求参数(可以认为是传给task的参数)
action:需要分发的事件(这里实际接收的就是refreshHomePageAction)

在执行完Promise容器中的内容后,将结果交给refreshHomePageAction

再回顾一下refreshHomePageAction的模样,它返回的是一个普通对象(可以理解成final action),task执行的结果最终变成这个普通对象的一部分


     
     
  1. const refreshHomePageAction = (data = {}) => {
  2. return {
  3. type: REFRESH_HOMEPAGE,
  4. data
  5. }
  6. }
最后执行dispatch方法,将这个final action分发出去,接下来就会执行reducer

声明组件的reducer
上代码


      
      
  1. export const homeIndex = (state = initHomeData(), action) => {
  2. printf( ‘homeIndex Reducer receive an action:’, action)
  3. switch (action.type) {
  4. case REFRESH_HOMEPAGE:
  5. return action.data
  6. default:
  7. return state
  8. }
  9. }

      
      
  1. initHomeData = () => {
  2. return {
  3. status: 200,
  4. serverTime: new Date().valueOf()
  5. }
  6. }
homeIndex方法接收state和action两个参数,state可以有初始值

redux是怎么会执行homeIndex这个方法的呢?
还记得在界面路由创建的store时传入的reducer吗?再贴一遍代码。


      
      
  1. const createStoreWithMiddleware = applyMiddleware(thunk)(createStore)
  2. const store = createStoreWithMiddleware(allReducers)
因为reducer按应用模块划分,所以每个模块的reducer都放在了allReducers中,看到homeIndex了吗?


      
      
  1. //所有的reducer
  2. export const allReducers = combineReducers({
  3. homeIndex,
  4. homeBanner,
  5. homeClassify,
  6. homeRecolist,
  7. meIndex
  8. });
正是因为创建store时,reducer是作为参数传入的,那么redux在dispatch一个action之后,redux当然知道要执行哪些reducer了(事实上传入的每个reducer都会被执行,最后会通过log验证这个事实)


绑定组件
主要就是mapXXXToProps那两个方法

上代码


      
      
  1. //state中包括了所有绑定组件的state,return的是当前组件需要放在props中的数据
  2. mapStateToProps = (state) => {
  3. return state.homeIndex
  4. }
  5. //return 的是组件中需要放在props中的方法
  6. mapDispatchToProps = (dispatch) => {
  7. return {
  8. refreshHomePage: (param) => refreshHomePage(dispatch, param)
  9. }
  10. }
  11. //绑定组件
  12. export default connect(
  13. mapStateToProps,
  14. mapDispatchToProps
  15. )(HomePage)
mapStateToProps中返回的就是 组件需要的数据( 从store的state中取的),最终这些数据会变成组件props的一部分

比如 state.homeIndex被某个reducer改成如下


     
     
  1. {
  2. status: 200,
  3. serverTime: 1314
  4. }
组件的props被更新后,就可以通过 this.props.status和this.props.serverTime得到这些值

mapDispatchToProps中返回的是一个对象,这个对象中每个属性的值是一个方法,属性名就是组件中使用this.props.XXX()调用的那个XXX,最终这些属性也会变成组件props的一部分

log验证
苍白的文字描述得再多,不如用铁打的事实来得直接

我们在可能有疑问的地方加上log

这里


     
     
  1. if (firstRender){
  2. this.logMsgWithKey(‘初始props’, this.props)
  3. } else{
  4. this.logMsgWithKey(‘新的props’, this.props)
  5. }
  6. firstRender = false
这里


     
     
  1. export const refreshHomePageTask = (dispatch, param = {}, action) => {
  2. printf(‘refreshHomePageTask’ + ‘_请求参数:’, param)
  3. return new Promise( (resolve, reject) => {
  4. resolve(initHomeData())
  5. }).then( (res) => {
  6. printf(‘refreshHomePageTask’ + ‘_请求结果:’, res)
  7. dispatch(action(res))
  8. })
  9. }
还有这里


     
     
  1. export const homeIndex = (state = initHomeData(), action) => {
  2. printf(‘homeIndex Reducer receive an action:’, action)
  3. switch (action.type) {
  4. case REFRESH_HOMEPAGE:
  5. return action.data
  6. default:
  7. return state
  8. }
  9. }
执行后得到如下铁证








评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

烈焰晴天

你的鼓励就是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值