有配套視頻
,能夠酌情觀看。
- 本篇資源:連接: https://pan.baidu.com/s/1cGjEua 密碼: scea
簡單來講,redux
就是幫咱們統一管理了 react
組件的 state
狀態。html
爲何要使用 redux
統一管理 state
呢?沒有 redux
咱們依舊能夠開發 APP,可是當 APP
的複雜度到達必定程度的時候,擺在咱們面前的就是 難以維護
的代碼(其中包含組件大量的異步回調,數據處理等等),可是使用 redux
也會增長咱們整個項目的複雜度,這就須要咱們在二者之間進行權衡了,對於這一部分,redux
開發者給咱們下面幾個參考點:react
如下幾種狀況不須要使用 redux
:npm
總體 UI 很簡單,沒有太多交互。redux
不須要與服務器進行大量交互,也沒有使用 WebSocket。react-native
視圖層只從單一來源獲取數據。api
如下幾種狀況可考慮使用 redux
:跨域
用戶的交互複雜。安全
根據層級用戶劃分功能。服務器
多個用戶之間協做。app
與服務器大量交互,或使用了 WebSocket。
視圖層須要從多個來源獲取數據。
遇到 React 沒法解決的問題。
總結以上內容:redux
適用於 多交互,多數據源,複雜程度高的工程中。
也就是說,當咱們的組件出現 某個狀態須要共享
,須要改變另外一個組件狀態
等傳值比較不容易的狀況。就能夠考慮 redux
,固然還有其餘 redux
的替代產品供咱們使用。
譯註:
WebSocket
:被稱爲下一代客戶端與服務端的異步通訊方法。取代了單個的TCP套接字,使用ws或wss協議,可用於任意的客戶端和服務器程序。WebSocket目前由W3C進行標準化。主要的優勢是服務器和客戶端能夠彼此相互推送信息,容許跨域通訊。
redux
以前,基本的東西仍是要都懂的,數據流向介紹:Action:行爲。它的做用就是將咱們更新組件的 狀態(state)
的每一個動做抽象爲一個行爲,它有一個必須的參數 type
,定義了 Action(行爲)
的名稱,其餘參數可自定義。寫法:
{ type: 'TEST_ACTION', key1: 'value', ... keyN: value }
由於 Action
是個對象,因此,咱們須要建立這個對象,那建立這個對象的方法叫作 ActionCreator
,寫法:
function testAction(key1: ?string, ..., keyN: ?string) { return { type: "TEST_ACTION", key1: key1, ... keyN: keyN } }
Reducer:reducer 的做用就是根據傳入的 Action行爲和舊的 state對象,返回一個新的 state ,而後組件會根據 state 刷新。當咱們肯定了組件的 state 對象結構 和 action 行爲的時候就能夠編寫 reducer 中的內容。寫法:
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 合成統一管理。
import { combineReducers } from 'redux'; import testReducer1 from './testReducer1'; import testReducer2 from './testReducer2'; export default = combineReducers({ testReducer1, testReducer2 });
不可修改傳入的參數。
必定要乾淨,沒有API請求,沒有變量修改,單純執行計算,沒有特殊狀況。
調用非純函數(Date.now()、Math.random()等),每次都會獲得不一樣結果致使數據錯誤等安全問題。
當傳入的 state 與 舊state 相比沒有區別,返回的 新state也應該一摸同樣。
Store:當 reducer 返回了新的 state 後,這個 state 怎麼傳到組件和存儲就成了問題,redux 就是把這個狀態統一放到 store 中進行管理。
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個 全局方法:
createStore
:建立一個readux store 來存儲應用中全部的state,應用中只能存在一個 store
createStore(reducer, [initialState],enhancer);
combineReducers
:把多個reducer函數做爲value的object,合併成一個reducers函數,而後就能夠經過reducers調用各個子reducer,state 對象的結構由傳入的多個 reducer 的 key 決定。
combineReducers(...reducers)
...middlewares
:每一個 middleware 接受 store 的 dispatch 和 getState 函數做爲命名參數,並返回一個函數。
該函數會被傳入被稱爲 next 的下一個 middleware 的 dispatch 方法,並返回一個接受 action 的新函數,這個函數能夠直接調用 next(action),或者在其餘須要的時刻調用,也可不調用。
調用鏈的最後一個 middleware 會接受真實的 store 的 dispatch 方法做爲 next 參數,並結束調用鏈。因此 middleware 的函數爲 ({ getState, dispatch }) => next => action。
返回值
:一個應用了 middleware 後的 store enhancer。這個store enhancer 就是一個函數,而且須要應用到 createStore。它會返回一個應用了 middleware 的新 createStore。
bindActionCreators
:把 actionCreators 轉曾擁有同名 keys 的對象,讓 dispatch 把每一個 actionCreator 包裝起來,這樣就能夠直接調用它們。惟一使用 bindActionCreators 的場景是須要把 actionCreator 往下傳到一個組件上,卻不想讓這個組件察覺到 redux 的存在,並且不但願把 redux store 或者 dispatch 傳給它。
// actionCreators:一個 actionCreators 或 鍵值是 actionCreators 的對象 // dispatch:一個 dispatch 函數, 由 store 提供 bindActionCreators(actionCreators, dispatch)
返回值
:一個與原對象相似的對象,只不過這個對象中的每一個函數值都直接 dispatch action。若是傳入的是個函數,返回的也是函數。compose(...fuctions)
:當須要多個 store 加強器 依次執行的時候使用它。compose 在應用常見的兩個用法:
// 1 let buildStore = compose( applymiddleware(thunk) )(createStore) // 2 let initStore = compose( applymiddleware(thunk) )
參數1(arguments):合成多個函數。每一個函數接受一個函數做爲參數,而後返回一個函數。
參數2(Function):從右往左把接受到的函數合成後的終極函數。
可能剛接觸,還不能很好理解,這邊咱們換個方式來理解,以下圖:
redux
的內容(如 redux數據異步處理等)可前往 官方文檔 閱讀查看,這邊不講這麼多,只要瞭解上面的這些就能夠了。終於進入正題了,爲了在 react-native
中使用 redux
,開發者提供了 react-redux
,基礎工做原理不變,只不過多了些方法和參數,因此這邊就須要繼續瞭解一下,如下內容整理自官方文檔:
<Provider store>
:使組件層級中的 connect() 方法可以獲得 redux store。正常狀況下,咱們的根組件應該嵌套在
屬性(store):工程中惟一的 redux store。
屬性(children):組件層級的根組件。
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options]):連接 react組件 和 redux store。
參數(mapStateToProps(state, [ownProps]): stateProps):定義了這個參數,組件會監聽 redux store 的變化,在任何狀況下,只要 redux store 發送變化, mapStateToProps 函數就會被調用。也就是說:mapStateToProps負責返回須要傳遞給子組件的 state。
這個函數必須返回一個純對象,這個對象會與組件的props合併,若是省略這個參數,組件將監聽不到 redux store 。
若是指定改回調函數中的第二個參數 ownProps,這個參數的值爲傳遞到組件的props,並且只要組件接到新的 props,mapStateToProps 也會被調用。
參數(mapDispatchToProps(dispatch, [ownProps]): dispatchProps):負責返回一個 dispatchProps,dispatchProps 是actionCreator的key和dispatch(action)的組合。
若是傳遞一個對象,那麼每一個定義在該對象的函數都將被當作 redux action creator,並且這個對象會與 redux store 綁定在一塊兒,其中所定義的方法名將做爲屬性名,合併到組件的 props 中。
若是傳遞的是一個函數,該函數將接收一個 dispatch 函數,而後由咱們本身決定如何返回一個對象,這個對象經過 dispatch 函數與 action creator 以某種方式綁定在一塊兒(提示:你也許會用到 Redux 的輔助函數bindActionCreators())。
若是你省略這個 mapDispatchToProps 參數,默認狀況下,dispatch 會注入到你的組件 props 中。
若是指定了該回調函數中第二個參數 ownProps,該參數的值爲傳遞到組件的 props,並且只要組件接收到新props,mapDispatchToProps 也會被調用。
參數(mergeProps(stateProps, dispatchProps, ownProps): props (Function)):若是指定了這個參數,mapStateToProps() 與 mapDispatchToProps() 的執行結果和組件自身的 props 將傳入到這個回調函數中。該回調函數返回的對象將做爲 props 傳遞到被包裝的組件中。你也許能夠用這個回調函數,根據組件的 props 來篩選部分的 state 數據,或者把 props 中的某個特定變量與 action creator 綁定在一塊兒。若是你省略這個參數,默認狀況下返回 Object.assign({}, ownProps, stateProps,dispatchProps) 的結果。
參數(options (Object)) 若是指定這個參數,能夠定製 connector 的行爲。
返回值:根據配置信息,返回一個注入了 state 和 action creator 的 React 組件。
靜態屬性:WrappedComponent (Component): 傳遞到 connect() 函數的原始組件類。
靜態方法:組件原來的靜態方法都被提高到被包裝的 React 組件。
實例方法:getWrappedInstance(): ReactComponent;僅當 connect() 函數的第四個參數 options 設置了 { withRef: true } 才返回被包裝的組件實例。
注:
函數將被調用兩次。第一次是設置參數,第二次是組件與 Redux store 鏈接 connect(mapStateToProps, mapDispatchToProps, mergeProps)(MyComponent)。
connect 函數不會修改傳入的 React 組件,返回的是一個新的已與 Redux store 鏈接的組件,並且你應該使用這個新組件。
mapStateToProps 函數接收整個 Redux store 的 state 做爲 props,而後返回一個傳入到組件 props 的對象。該函數被稱之爲 selector。參考使用 reselect 高效地組合多個 selector ,並對 收集到的數據進行處理。
bindActionCreators 的做用就是將 Actions 和 dispatch 組合起來生成 mapDispatchToProps 須要生成的內容。
使用 redux
以前,咱們仍是須要配置一下是吧,很簡單,咱們只須要執行如下步驟:
使用 終端
打開須要使用 redux
的工程主目錄:
// 好比咱們的 cd Desktop/Test
導入 redux庫
:
npm install --save redux
我喜歡直接介紹實用的,因此這邊咱們要直接介紹 react-redux
,不磨磨唧唧一大堆有的沒的,因此咱們還須要:
npm install --save react-redux
這裏先不講 中間件
,儘可能否則這些東西干擾咱們。
好了,這樣咱們就能夠開始在 react-native
中 使用 redux
了。
redux官方文檔的示例
咱們能夠看出官方建議咱們將組件分紅 containers(容器組件)
、components(模塊視圖組件)
、redux
三大塊。因此咱們這邊文件的層級以下圖所示:接着,咱們再來完成視圖部分,而後根據視圖部分肯定哪些須要 redux 支持,再來生成相應的 action
與 reducer
文件。
首先,是 Main
文件,做爲咱們的容器組件放到 containers
文件夾內,Main
中的內容:
import React, { Component } from 'react'; import { StyleSheet, Text, View, TouchableOpacity, } from 'react-native'; export default class Main extends Component { render() { return ( <View style={styles.container}> {/* 須要改變的組件 */} {/* 按鈕 */} <TouchableOpacity> <Text>改變文字按鈕</Text> </TouchableOpacity> </View> ); } } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#F5FCFF', }, });
那裏面咱們須要將 Text
做爲視圖組件獨立出來,因此將視圖組件 TestText
放到 components
文件夾中,TestText
中的內容:
export default class TestText extends Component { render() { return ( <Text>Welcome to React Native</Text> ); } }
視圖部分咱們搭建完成,那麼咱們接着就是肯定須要哪些 action(行爲)
,那前面提到了,咱們是要點擊按鈕的時候讓文字發生改變,也就是說咱們當前須要一個改變文字的行爲,那咱們就將這個行爲命名爲 CHANGE_TEXT
,那麼咱們須要初始化這個 action 這個對象,也就是前面咱們提到的 action creator
:
export const CHANGE_TEXT = 'CHANGE_TEXT'; // 初始化 CHANGE_TEXT 對象 export const changeText = (text) => { return { type: CHANGE_TEXT, text } };
action
文件配置完畢後,咱們就能夠根據需求來編寫 reducer
文件了,reducer
文件就是起到更新 state
的做用嘛,因此咱們將改變 文字 的邏輯放到這裏,當reducer
匹配到當前的點擊行爲爲 CHANGE_TEXT
時,就執行相應的操做,返回一個新的 state
給咱們使用,若是匹配不到,那麼就默認返回一個不變的新 state
:
import { CHANGE_TEXT, changeText } from '../action/action'; const mainReducer = (state = changeText('welcome to React Native'), action) => { const newState = state; const text = action.text; // 判斷 action 類型 switch (action.type) { case CHANGE_TEXT: return { ...newState, text: '改變了' + text }; default: return { ...newState, text:state.text } } }; export default mainReducer;
配置完 action
和 reducer
兩個文件後,緊接着咱們就能夠根據 reducer
來初始化 store
了:
import Reducer from '../reducer/reducer'; import { createStore } from 'redux'; export default () => { // 根據 reducer 初始化 store const store = createStore(Reducer); return store; }
redux
的東西已經都配置完成了,接着就剩下使用了,因此接下來要解決的問題就是怎麼發送行爲,怎麼接收 state(狀態)
,上面提到了,store
實際上是個方法集,咱們的 發送行爲 和 接收狀態
方法都在 store
中,因此只要拿到 store
,因此只要拿到 store
就能進行這兩個操做。
那怎麼拿到 store
呢?在官方文檔中,清楚地告訴咱們,Provider
的任務就是將 store
傳給 connect
,而 connect
的做用是將咱們的組件進行第二次包裝,將操做數據的函數和數據的狀態包裝到 props
中,因此,首先,咱們須要對咱們的 Main
文件進行第一次包裝,咱們再新建一個 index
文件來對 Main
文件進行包裝:
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> ) } }
包裝完成後,咱們的 Main
文件就能夠得到 store
了,那接着就是進行第二次包裝了,經過 connect
生成新組件:
import React, { Component } from 'react'; import { StyleSheet, Text, View, TouchableOpacity, } from 'react-native'; import { connect } from 'react-redux'; import { changeText } from '../redux/action/action'; import TestText from '../components/TestText'; class Main extends Component { render() { // 經過 props 拿到保存的 onChangeText const { onChangeText } = this.props; return ( <View style={styles.container}> {/* 須要改變的組件 */} <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 變化 } }; // 發送行爲 const mapDispatchToProps = (dispatch) => { return { // 發送行爲 } }; // 進行第二層包裝,生成的新組件擁有 接收和發送 數據的能力 export default connect(mapStateToProps, mapDispatchToProps)(Main);
到這裏,咱們的 新組件 就可以收發數據了,那怎麼接收和發送呢,別急,咱們接着就來完成 mapStateToProps(更新回調) 和 mapDispatchToProps(發送行爲)
兩個方法。首先,咱們須要經過 mapDispatchToProps
來發送行爲,而後經過 mapStateToProps
來監聽 state
的變化,這邊咱們須要發送的行爲 type
是 CHANGE_TEXT
,當發送行爲以後,reducer
就會去匹配 行爲的類型,進行相應操做:
// 發送行爲 const mapDispatchToProps = (dispatch) => { return { onChangeText: () => dispatch(changeText('外部傳值')), } };
當 reducer
接收到咱們觸發的 行爲 並進行一系列處理後,最終會返回一個新的 state
,那麼 就會自動調用 mapStateToProps
來告訴系統,state
被操做了,那麼咱們就能夠經過 mapStateToProps
來獲取 state
狀態:
// 獲取 state 變化 const mapStateToProps = (state) => { return { value: state.text, } };
那麼接下來咱們 怎麼改變文字 呢?前面提到,connect 做用就是生成一個新的組件,新的組件的 props
中包含了數據獲取和操做數據的函數,因此咱們須要讓 子組件拿到容器組件中的 props
,而後在 子組件 中經過 props
就能夠拿到上面 定義的 value 和 onChangeText:
export default class TestText extends Component { render() { // 獲取 props 中的 value const { value } = this.props; return ( // 根據 value 改變內部文字 <Text>{value}</Text> ); } }
到這裏,咱們就能成功改變文字了。
小結論:
其實從上面的 demo 就能夠看出,使用了
redux
的項目變得比本來要複雜得多,本來幾句代碼就能搞定的事情如今要來個山路十八彎
,這是由於 redux 是爲了解決複雜工程而孕育的,因此不要爲了使用 redux 而去使用它,使用以前須要權衡一下利弊,其中的好與壞只能本身慢慢體會。redux 對於剛入門的朋友來講確實比較繞,幫助理解的辦法就是多練,若是隻看的話可能會越看越亂,因此仍是建議多練,熟練以後就感受沒什麼了。
我我的認爲 中間件 只須要注意 「順序」 就能夠了。使用方法什麼的在 中間件的說明文檔 中都講得很清楚。
關於 中間件 的使用,這邊就很少講了,由於可用的 中間件 不少,不可能一個一個講,等後面文章涉及哪些 中間件 再講。