react-native 之 redux 與 react-redux

1.下載插件javascript

npm install redux --save

npm install react-redux --save

2.項目結構java

3.redux 適用於 多交互,多數據源,複雜程度高的工程中。react

redux 必要知識

使用 redux 以前,基本的東西仍是要都懂的,數據流向介紹:android

 

Action:行爲。它的做用就是將咱們更新組件的 狀態(state) 的每一個動做抽象爲一個行爲,它有一個必須的參數 type,定義了 Action(行爲) 的名稱,其餘參數可自定義。寫法:npm

{
    type: 'TEST_ACTION',   // 定義Action的名稱
    key1: 'value',
        ...
    keyN: value
}

 

由於 Action 是個對象,因此,咱們須要建立這個對象,那建立這個對象的方法叫作 ActionCreator,寫法:redux

function testAction(key1: ?string, ..., keyN: ?string) {
    return {
        type: "TEST_ACTION",
        key1: key1,
        ...
        keyN: keyN
    }
}

 

Reducerreducer 的做用就是根據傳入的 Action行爲和舊的 state對象,返回一個新的 state ,而後組件會根據 state 刷新。當咱們肯定了組件的 state 對象結構 和 action 行爲的時候就能夠編寫 reducer 中的內容。寫法:react-native

function testReducer(state, action) {
    let key1 = action.key1;
    switch(action.type) {
        case TEST_ACTION:
            return {
            ...state,
            key1: key1 + '變化'
        };

        default:
            return state;
    }
};

export default testReducer;

 

固然咱們的工程中可能會有多個 reducer 的狀況,經過 combineReducers 能夠將多個 reducer 合成統一管理。app

import { combineReducers } from 'redux';
import testReducer1 from './testReducer1';
import testReducer2 from './testReducer2';

export default = combineReducers({
    testReducer1,
    testReducer2
});

 

Store當 reducer 返回了新的 state 後,這個 state 怎麼傳到組件和存儲就成了問題,redux 就是把這個狀態統一放到 store 中進行管理。ide

import { createStore } from 'redux';
const store = createStore(reducers);

 

上面的代碼根據 reducers 建立了一個 store方法集(它並非一個對象),而後再 store 中提供一些方法供咱們使用:函數

// 獲取當前 state
store.getState()

// 發送action,根據咱們前面 註冊的reducers 處理state
store.dispath(action)

// 替換當前 state 中的 reducer
store.replaceReducer(nextReducer) 

// 添加監聽
store.subscribe(listener)

 

注:

另外 redux 有 5個 全局方法:

(1)createStore:建立一個redux store 來存儲應用中全部的state,應用中只能存在一個 store

createStore(reducer, [initialState],enhancer);

(2)combineReducers:把多個reducer函數做爲value的object,合併成一個reducers函數,而後就能夠經過reducers調用各個子reducer,state 對象的結構由傳入的多個 reducer 的 key 決定。

combineReducers(...reducers)

(3)...middlewares:每一個 middleware 接受 store 的 dispatch 和 getState 函數做爲命名參數,並返回一個函數。

1.該函數會被傳入被稱爲 next 的下一個 middleware 的 dispatch 方法,並返回一個接受 action 的新函數,這個函數能夠直接調用 next(action),或者在其餘須要的時刻調用,也可不調用。

2.調用鏈的最後一個 middleware 會接受真實的 store 的 dispatch 方法做爲 next 參數,並結束調用鏈。因此 middleware 的函數爲 ({ getState, dispatch }) => next => action。

3.返回值:一個應用了 middleware 後的 store enhancer。這個store enhancer 就是一個函數,而且須要應用到 createStore。它會返回一個應用了 middleware 的新 createStore。

(4)bindActionCreators:把 actionCreators 轉曾擁有同名 keys 的對象,讓 dispatch 把每一個 actionCreator 包裝起來,這樣就能夠直接調用它們。惟一使用 bindActionCreators 的場景是須要把 actionCreator 往下傳到一個組件上,卻不想讓這個組件察覺到 redux 的存在,並且不但願把 redux store 或者 dispatch 傳給它。

// actionCreators:一個 actionCreators 或 鍵值是 actionCreators 的對象
// dispatch:一個 dispatch 函數, 由 store 提供
bindActionCreators(actionCreators, dispatch)

返回值:一個與原對象相似的對象,只不過這個對象中的每一個函數值都直接 dispatch action。若是傳入的是個函數,返回的也是函數。

(5)compose(...fuctions):當須要多個 store 加強器 依次執行的時候使用它。compose 在應用常見的兩個用法:

// 1
let buildStore = compose(
    applymiddleware(thunk)
)(createStore)

// 2
let initStore = compose(
    applymiddleware(thunk)
)

參數1(arguments):合成多個函數。每一個函數接受一個函數做爲參數,而後返回一個函數。

參數2(Function):從右往左把接受到的函數合成後的終極函數。

換張圖理解:

 

react-redux 須要知道的那些事

react-redux 使用

(1)首先,根據 redux官方文檔的示例 咱們能夠看出官方建議咱們將組件分紅 containers(容器組件)components(模塊視圖組件)redux 三大塊。因此咱們這邊文件的層級以下圖所示:

adb version
adb devices

react-native init Test
cd Test
npm install --save redux
npm install --save react-redux
react-native start
react-native run-android

 

ipconfig
8081端口

 

(2)接着,咱們再來完成視圖部分,而後根據視圖部分肯定哪些須要 redux 支持,再來生成相應的 action 與 reducer 文件。

1.首先,是 Main 文件,做爲咱們的容器組件放到 containers 文件夾內,Main 中的內容:

Main.js

/*主頁面*/
import React, { Component } from 'react';
import {
    StyleSheet,
    Text,
    View,
    TouchableOpacity,
} from 'react-native';

// 引入 測試組件
import TestText from '../components/TestText'
 
export default class Main extends Component {
    render() {
        return (
            <View style={styles.container}>
                {/* 須要改變的組件 */}
                <TestText />
                
                {/* 按鈕 */}
                <TouchableOpacity>
                    <Text>改變文字按鈕</Text>
                </TouchableOpacity>
            </View>
        );
    }
}
 
const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
    },
});

那裏面咱們須要將 Text 做爲視圖組件獨立出來,因此將視圖組件 TestText 放到 components 文件夾中,TestText 中的內容:

TestText.js

/*測試組件*/
import React, { Component } from 'react';
import {
  Platform,
  StyleSheet,
  Text,
  View
} from 'react-native';
 
export default class TestText extends Component {
    render() {
        return (
            <Text>Welcome to React Native</Text>
        );
    }
}

(3)視圖部分咱們搭建完成,那麼咱們接着就是肯定須要哪些 action(行爲),那前面提到了,咱們是要點擊按鈕的時候讓文字發生改變,也就是說咱們當前須要一個改變文字的行爲,那咱們就將這個行爲命名爲 CHANGE_TEXT,那麼咱們須要初始化這個 action 這個對象,也就是前面咱們提到的 action creator:

action.js

/**
 * 步驟一
 * 行爲 action
 */

// 定義行爲名稱
export const CHANGE_TEXT = 'CHANGE_TEXT';

// 初始化 CHANGE_TEXT 對象
export const changeText = (text) => { // 接收test參數
    return {
        type: CHANGE_TEXT, // 名稱
        text // 參數 默認值
    }
};

(4)action 文件配置完畢後,咱們就能夠根據需求來編寫 reducer 文件了,reducer 文件就是起到更新 state 的做用嘛,因此咱們將改變 文字 的邏輯放到這裏,當reducer 匹配到當前的點擊行爲爲 CHANGE_TEXT 時,就執行相應的操做,返回一個新的 state 給咱們使用,若是匹配不到,那麼就默認返回一個不變的新 state:

reducer.js

/**
 * 步驟二
 * 操做
 * 經過reducer操做action(根據action行爲建立reducer文件)
 */

/**
 * 引入 action
 * CHANGE_TEXT 類型(行爲名稱)
 * changeText 值
 */
import { CHANGE_TEXT, changeText } from '../action/action';

/**
 * 建立 reducer
 * 根據名稱判斷是哪個行爲
 * state = changeText('welcome to React Native') 初始化state
 */
const mainReducer = (state = changeText('welcome to React Native'), action) => {
 	/**
 	 * state 不能直接改變
 	 * 定義newState 接收state的值
 	 */
    const newState = state;
    const text = action.text;
 
    // 判斷 action 類型
    switch (action.type) {
        case CHANGE_TEXT:
            return {
            	// 返回全部的newState
                ...newState,
                text: '改變了' + text
            };
 
        default:
            return {
                ...newState,
                text:state.text
            }
    }
};

// 輸出口
export default mainReducer;

(5)配置完 action 和 reducer 兩個文件後,緊接着咱們就能夠根據 reducer 來初始化 store 了:

store.js

/**
 * 步驟三
 * 初始化 store
 */
// 引入 reducer(操做)
import Reducer from '../reducer/reducer';
// 獲取redux中的初始化方法 createStore
import { createStore } from 'redux';

// 輸出
export default () => {
 
    // 根據 reducer 初始化 store
    const store = createStore(Reducer);
 
    return store;
}

(6)redux 的東西已經都配置完成了,接着就剩下使用了,因此接下來要解決的問題就是怎麼發送行爲,怎麼接收 state(狀態),上面提到了,store 實際上是個方法集,咱們的 發送行爲 和 接收狀態 方法都在 store 中,因此只要拿到 store,因此只要拿到 store 就能進行這兩個操做。

(7)那怎麼拿到 store 呢?在官方文檔中,清楚地告訴咱們,Provider 的任務就是將 store 傳給 connect,而 connect 的做用是將咱們的組件進行第二次包裝,將操做數據的函數和數據的狀態包裝到 props 中,因此,首先,咱們須要對咱們的 Main 文件進行第一次包裝,咱們再新建一個 index 文件來對 Main 文件進行包裝:

Index.js

/**
 * 容器組件
 * 入口文件
 */
import React, { Component } from 'react';
 
// 引用外部文件
import { Provider } from 'react-redux';
import Main from './Main';
import configureStore from '../redux/store/store';
 
// 調用 store 文件中的 mainReducer常量中保存的方法
const store = configureStore();
// 定義根組件
export default class Root extends Component {
    render() {
        return(
            // 第一層包裝,爲了讓 main 可以拿到 store
            <Provider store={store}>
                <Main />
            </Provider>
        )
    }
}

(8)包裝完成後,咱們的 Main 文件就能夠得到 store 了,那接着就是進行第二次包裝了,經過 connect 生成新組件:

Main.js

/*主頁面*/
import React, { Component } from 'react';
import {
    StyleSheet,
    Text,
    View,
    TouchableOpacity,
} from 'react-native';

// 引入 測試組件
import TestText from '../components/TestText'
/**
 * 獲取 react-redux的 connect() 方法
 * 注:使組件層級中的 connect() 方法可以獲得 redux store
 */
import { connect } from 'react-redux';
// 獲取 action行爲的值
// import { CHANGE_TEXT } from '../redux/action/action';
import { changeText } from '../redux/action/action';
 
class Main extends Component {
    render() {
        // 經過 props 拿到保存的 onChangeText
        const { onChangeText } = this.props;
        
        return (
            <View style={styles.container}>
                {/* 須要改變的組件 */}
                {/* 將父組件(Main)的props,傳遞給子組件(TestText)*/}
                <TestText {...this.props} />
                
                {/* 按鈕 */}
                <TouchableOpacity
                    // 設置按鈕點擊事件
                    onPress={onChangeText}
                >
                    <Text>改變文字按鈕</Text>
                </TouchableOpacity>
            </View>
        );
    }
}
 
const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
    },
});

/************ 初始化 ************/
// 獲取 state 變化
const mapStateToProps = (state) => {
    return {
        // 獲取 state 變化
        value: state.text,
    }
};
 
// 發送行爲
const mapDispatchToProps = (dispatch) => {
    return {
        // 發送行爲
        // onChangeText: () => dispatch({type: CHANGE_TEXT}),
        onChangeText: () => dispatch(changeText('外部傳值')),
    }
};
 
/**
 * 經過 connect() 方法 對Main組件進行第二層包裝
 * 進行第二層包裝,生成的新組件擁有 接收和發送 數據的能力
 * mapStateToProps 獲取狀態的函數
 * mapDispatchToProps 發送行爲的函數
 */
export default connect(mapStateToProps, mapDispatchToProps)(Main);

(9)到這裏,咱們的 新組件 就可以收發數據了,那怎麼接收和發送呢,別急,咱們接着就來完成 mapStateToProps(更新回調) 和 mapDispatchToProps(發送行爲) 兩個方法。首先,咱們須要經過 mapDispatchToProps 來發送行爲,而後經過 mapStateToProps 來監聽 state 的變化,這邊咱們須要發送的行爲 type 是 CHANGE_TEXT,當發送行爲以後,reducer 就會去匹配 行爲的類型,進行相應操做:

// 發送行爲
const mapDispatchToProps = (dispatch) => {
    return {
        onChangeText: () => dispatch(changeText('外部傳值')),
    }
};

(10)當 reducer 接收到咱們觸發的 行爲 並進行一系列處理後,最終會返回一個新的 state,那麼 就會自動調用 mapStateToProps 來告訴系統,state 被操做了,那麼咱們就能夠經過 mapStateToProps 來獲取 state 狀態:

// 獲取 state 變化
const mapStateToProps = (state) => {
    return {
        value: state.text,
    }
};

(11)那麼接下來咱們 怎麼改變文字 呢?前面提到,connect 做用就是生成一個新的組件,新的組件的 props 中包含了數據獲取和操做數據的函數,因此咱們須要讓 子組件拿到容器組件中的 props,而後在 子組件 中經過 props 就能夠拿到上面 定義的 value 和 onChangeText:

TestText.js

/*測試組件*/
import React, { Component } from 'react';
import {
  StyleSheet,
  Text,
  View
} from 'react-native';
 
export default class TestText extends Component {
    render() {
        // 獲取 props 中的 value
        const { value } = this.props;
 
        return (
            // 根據 value 改變內部文字
            <Text>{value}</Text>
        );
    }
}

(12)效果圖

注:轉載 http://blog.csdn.net/yeshaojian/article/details/70599572

.

相關文章
相關標籤/搜索