【ReactNative】react-redux使用心得

Redux

Redux 单项数据流框架,其特色是严格的数据流控制,开发过程当中应遵循3个原则:react

  • 惟一数据源
  • 保持状态只读
  • 数据改变只能经过纯函数
1. 惟一数据源

Redux 应用中应保持数据源的惟一性,说白了整个应用中只保持一个 Store,全部组件的数据源就是这个 Store 上的状态,Store 是个树形结构,每每某一个组件或者模块的数据来源于 Store 上的某个节点。git

2. 保持状态只读

Redux 强调要改变 Store 的状态只能经过 actionaction 返回对象,提供给 Redux 完成新的状态的组装。github

3. 数据改变只能经过纯函数

这里的纯函数就是 Reducer ,其函数签名包含两个参数 stateaction,顾名思义就是经过 action 去改变 state,通常来讲是返回一个新的 state,其函数签名大体以下export default (state = initialState, action) => {}redux

总结一下

Reudx 包含 StoreStateReudceraction,主要为这四部分组成:bash

  • Store 一个全局的对象, 其包含 Reudcer ,是一个树形结构,每一个 Reducer 算一个节点。
  • Reducer 返回一个新的状态,简单来讲,经过什么样的 action 产生一个改变了某一个节点的 State
  • State 状态树,一样树形结构,能够理解为 Reducer 下面的子节点,state 的设计应尽可能扁平,一个模块控制一个状态节点、避免冗余数据。
  • action 返回一个对象, 供 Reducer 使用。 action 函数返回的对象大体以下{ type: actionType, data: ooxx };这里说一下 actionType:一个常亮,用来区分不一样的 action

聪明组件&傻瓜组件(容器组件&展现组件)

所谓聪明、傻瓜只是相对来讲,一样也叫容器组件和展现组件。 鉴于专业性,下文一概采用容器组件和展现组件的叫法。容器组件负责将 Store 中的状态,经过 props 传递给展现组件,展现组件只负责渲染页面,无需持有状态。将一个组件拆分为容器组件和展现组件是设计 React 组件的一种模式,和 Redux 无关。框架


前二者的结合 react-redux

上面讲到,Redux 负责管理状态,组件拆分为容器组件和展现组件。容器组件须要状态,状态来自哪呢?固然是 Redux 。 故,能够将 Redux 和组件作一个结合,达到更好的效果,那就是 react-redux,他帮助开发者抽取了可复用的容器组件,开发者只需关注展现组件便可。 相比于 Redux ,react-redux 多了 Providerconnect 两部分ide

Provider

理解 Provider 首先要知道 context ,也就是上下文。试想这样一种业务场景:一个多层嵌套的组件结构中,只有最里层的组件须要使用 store ,首先想到的是用 props 传递,为了把 store 从最外层传递到最里层,就要中间的全部组件都增长对 store的支持 ,这样无疑是灾难,很麻烦。React 提供了 context 的支持,就是说一个树型结构中的全部组件都能访问一个对象(上下文)。为了让树形结构支持 context ,须要父组件声明对 context 的支持, Provider 就提供了这样的功能(也是一个组件,增长了 context)。因此咱们会看到 Provider 须要传递一个 store 属性,而且位于根组件的位置,这样应用中全部的组件都会经过 context 共享这个 store 。react-redux 提供了建立 Store 的方法。函数

connect

一个函数,负责展现组件和容器组件的链接。大体是这样export default connect(mapStateToProps, mapDispatchToProps)(SearchBar) 这里边实际上是两次函数的执行,首先 connect 函数执行并返回了另外一个函数而后执行,参数是展现组件。flex

  • mapStateToProps 一个返回对象的函数,可选的。经过函数签名能够知道是将 Store 中的某一个 State 转化为展现组件所须要的 props,决定暂时组件显示什么样的数据。
  • mapDispatchToProps 一个返回对象的函数,可选的。展现组件不能只负责展现,固然也有必定的交互,也就是触发 action 。在 react-redux 中触发一个 action 是经过 dispatch 执行的。因此这个函数是将 dispatch 转换为 props 供展现组件使用。

其实 connect 在执行的过程当中实际上产生了一个无名的 React 组件,这个组件定制了 shouldComponentUpdate 函数的实现,实现逻辑是比对此次传递给内层展现组件的 props 和上一次的 props ,由于负责“组件看起来怎样”的展现组件是一个无状态的组件,他的渲染彻底由传入的 props 决定,若是 props 没有变化,那就能够认为渲染结果确定同样。就不须要通过 VirtualDOM 作渲染。ui

总结一下

不难理解 react-redux 告诉咱们,展现组件仅仅负责展现,不须要持有任何状态,展现组件的全部 stateaction 所有来源于 props ,容器组件经过 props 传递给展现组件。


示例代码

Demo地址
  • actionType
export const HOMEPAGE_SHOWSEARCHBAR = 'HOMEPAGE/SHOWSEARCHBAR';

export const HOMEPAGE_MENU_PAGECHANGE = 'HOMEPAGE/MENU/PAGECHANGE';

复制代码
  • action
export const searchBarFetch = (text) => ({
  type: HOMEPAGE_FETCH_SEARCHBAR,
  text: text
});

export const updateHomePageMenuPage = (page) => ({
  type: HOMEPAGE_MENU_PAGECHANGE,
  currentPage: page
});
复制代码
  • State (我自认为不是很扁平,懒着改了)
const initialState = {
    homepage: {
        menuInfo: {
            items: common.menuInfos,
            currentPage: 0
        },
        gridInfos: [],
        sections: [{
            title: '',
            data: []
        }],
    },

    searchBar: {
        text: '搜一下'
    }
};
复制代码
  • Reducer Reducer 能够是多个,通常一个模块一个 Reducer
export default (state = initialState, action) => {
    switch (action.type) {
        case HOMEPAGE_FETCH_SEARCHBAR:
            {
                return {
                    ...state,
                    searchBar: {
                        text: action.text
                    }
                };
            }

        case HOMEPAGE_MENU_PAGECHANGE:
            {
                return {
                    ...state,
                    homepage: {
                        menuInfo: {
                            items: state.homepage.menuInfo.items,
                            currentPage: action.currentPage
                        },
                        gridInfos: state.homepage.gridInfos,
                        sections: state.homepage.sections
                    }
                }
            }
    }
    return state;
};
复制代码
  • Store react-redux 提供了 合并多个 Rducer 的方法,和建立 Store 的方法
const reducers = combineReducers({
    homepageReudcer
});

export default createStore(reducers);
复制代码
  • 展现组件
class HomeMenu extends Component {

    render() {

        const {menuInfos, currentPage} = this.props;

        const items = menuInfos.map(({title, icon}) => (<HomeMenuItem title={title} icon={icon} key={title}/>));

        const pageCount = Math.ceil(items.length / 10);
        let menuViews = [];
        for (let i = 0; i < pageCount; i++) {
            const itemSlices = items.slice(i * 10, i * 10 + 10);
            const view = <View style={styles.itemsView} key={i}>
                {itemSlices}
            </View>
            menuViews.push(view);
        }

        return (
            <View style={styles.container}>
                <ScrollView
                    horizontal
                    pagingEnabled={true}
                    showsHorizontalScrollIndicator={false}
                    onScroll={this._onScroll}>
                    {menuViews}
                </ScrollView>
                <PageControl
                    style={styles.pageControl}
                    numberOfPages={pageCount}
                    currentPage={currentPage}
                    currentPageIndicatorTintColor={color.primary}
                    pageIndicatorTintColor={color.gray}/>
                <View style={styles.line}/>
                <HomeGridView/>
                <View style={styles.line}/>
            </View>

        );
    }

    _onScroll = (event) => {
        const x = event.nativeEvent.contentOffset.x;
        const page = Math.round(x / common.screen.width);

        const {currentPage, setCurrentPage} = this.props;
        if (currentPage !== page) {
            setCurrentPage(page);
        }
    }
}

const styles = StyleSheet.create({
    container: {
        backgroundColor: 'white',
    },
    itemsView: {
        flexDirection: 'row',
        flexWrap: 'wrap',
        width: common.screen.width
    },
    pageControl: {
        margin: 10
    },
    line: {
        backgroundColor: color.paper,
        width: common.screen.width,
        height: 10,
    }
});

const mapStateToProps = (state) => ({menuInfos: state.homepageReudcer.homepage.menuInfo.items, currentPage: state.homepageReudcer.homepage.menuInfo.currentPage});
const mapDispatchToProps = (dispatch) => ({
    setCurrentPage: (page) => {
        dispatch(updateHomePageMenuPage(page));
    }
})

export default connect(mapStateToProps, mapDispatchToProps)(HomeMenu)

复制代码

完!!!

相关文章
相关标签/搜索