React-Native学习笔记——react-redux最佳实践应用篇

效果图

典型的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)
mapStateToProps中返回的就是组件需要的数据(从store的state中取的),最终这些数据会变成组件props的一部分

比如 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
    }
}
执行后得到如下铁证








阅读更多
个人分类: React-Native
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭