【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)

複製代碼

完!!!

相關文章
相關標籤/搜索