demo 地址:
https://github.com/chris6605/react-native-redux
1 >> 什么是redux ? 你的項目需不需要redux?
有個大神說過? 如果你覺得你的項目不需要redux, 那么你就真的不需要去用 redux.
redux 到底是什么呢? 其實就是用來統一管理整個應用 State 的, 將所有的 state 變化進行統一的流程處理, 使應用的 state變化清晰可見.
如圖 : 使用redux和不使用組件之間的通信方式的不同
在React中,數據在組件中是通過 props 單向流動的。數據從父組件流向子組件,由於這個特征,兩個兄弟組件之間的通信並不是那么清楚。
React並不建議直接采用組件到組件的通信方式,需要提一點的是, 在使用 redux 之后你就不能直接調用組件的方法了, 比如你寫個彈窗有個 show() 方法, 你在使用的地方通過引用 this.xxxDialog.show()是不會起作用的, 需要嚴格遵守單向數據流的思想. 盡管它有一些特性可以支持這么做(比如先將子組件的值傳遞給父組件,然后再由父組件在分發給指定的子組件)。這被很多人認為是糟糕的實踐方式,因為這樣的方式容易出錯而且會讓代碼向“拉面”一樣不容易理解。
當然React也沒有直接建議如何去處理這種情形, React 官方是這么解釋的:
對於非父子關系的組件,你可以自己建立一個全局的事件系統,Flux的模式也是一種可行的方式。
Redux的出現就讓這個問題的解決變得更加方便了。
Redux提供一種存儲整個應用狀態到一個地方的解決方案(可以理解為統一狀態層),這個存儲所有應用狀態的地方稱為“store”,組件發生事件時不會再 setState, 而是由 store 分發(dispatch)一個事件(action), 組件將狀態的變化通知給store,而不是直接通知其它的組件, store會根據 action 的類型來調用對應的 reducer(純函數, 告訴 state 該做出什么變化), reducer 會根據 action 的類型, 接收一個舊的 state, 返回一個新的 state, store 會收集到所有 reducer的state, 最后更新 state, 組件內部依賴的state的變化情況就可以通過訂閱 store 來實現。如圖所示:
使用Redux,所有的組件都從store里面獲取它們依賴的state,同時也需要將state的變化告知store。組件不需要關注在這個store里面其它組件的state的變化情況,Redux讓數據流變得更加簡單。這種思想最初來自Flux,它是一種和React相同的單向數據流的設計模式。
如果你的頁面單一頁面, 功能簡單, 狀態不多變, 沒有涉及多個頁面之間的交互, 那么你就不用往下看了, 你真的不需要用 redux這么重量級的東西, 但是比如做多語言, 換膚功能, 字體切換也可以考慮用redux.
redux 的三原則
1. Single source of truth
單一數據源。整個應用的state,存儲在唯一一個object中, 那就是 store, 一個應用有且只有一個 store
2. State is read-only
狀態是只讀的。唯一能改變state的方法,就是觸發action操作。不要想着再去 setState 了, 用了 redux 之后你改變 state 唯一的方法就是 dispatch 一個 action, action是什么? 別急,下面再說
3. Changes are made with pure functions
在改變state tree時,用到action,同時也需要編寫對應的reducers才能完成state改變操作。並且 reducer 一定要是一個純函數, 不要在里面做一些亂七八糟的騷操作
2 >> 什么是 Action? reducer ? store ? provider?
Action 動作 :比如登錄操作 退出登錄操作 添加待辦 刪除待辦都可以定義為一個 action
example:

這就是一個完整的 store 的創建過程, createStore 是redux 里提供的函數, 接受初始狀態, reducer( reducer 可以有多個, 這里是合並多個 reducer 的 RootReducer ), 中間件.
store 有什么用呢? store 就是一個保存着整個 APP 的所有state 的對象, 顧名思義, 就是一個倉庫嘛, 在任何一個組件中都可以通過store 來獲取任何一個 state,
Reducer 是一個純函數, 不要在這里做邏輯操作的事情, 他就是根據一個 action 的 type 返回一個新的 state 一定要保證其純潔性
異步Action
// 異步的Action 需要返回的是一個 function 在 createStore 時由middleWare 做處理 普通的action 直接返回純對象即可 | |
export function requireLogin(info) { | |
return LoginApi(info); | |
} |
這其實就是一個 action(只不過是個異步的 action , 如下圖上面是一個普通的同步 action , dispatch 這個 action 馬上就執行, 返回新的 state, UI 更新, 但是異步的不同, 不能馬上獲取新的 state
這里模仿請求的百度, 請求完成登錄成功, 異步的 action 返回的不是純對象了, 而是一個函數, 就是上圖那個函數, 另外在創建 store 的時候還要使用middleware 中間件, 這樣才能執行異步操作)
廢話都說的差不多了, 相信你對 redux大概有個了解了, 起碼對 action reducer store 這三個概念有個印象, action 就是描述一件事情發生的對象, reducer 就是個純函數, 接受舊的 state 和一個 action, 返回一個新的 state 到 store, store 就是全局唯一的一個倉庫,存儲着應用所有的 state
Provider
provider主要有兩個功能
- 在原應用組件上包裹一層,使原來整個應用成為Provider的子組件
- 接收Redux的store作為props,通過context對象傳遞給子孫組件上的connect
context
可以使子孫組件直接獲取父級組件中的數據或方法,而無需一層一層通過props向下傳遞。context
對象相當於一個獨立的空間,父組件通過getChildContext()
向該空間內寫值;定義了contextTypes
驗證的子孫組件可以通過this.context.xxx
,從context對象中讀取xxx字段的值。
總而言之,Provider模塊的功能很簡單,從最外部封裝了整個應用,並向connect模塊傳遞store。
而最核心的功能在connect模塊中。
connect的作用:
1、connect通過context獲取Provider中的store,通過store.getState()獲取整個store tree 上所有state。
2、connect模塊的返回值wrapWithConnect為function。
3、wrapWithConnect返回一個ReactComponent對象Connect,Connect重新render外部傳入的原組件WrappedComponent,並把connect中傳入的mapStateToProps, mapDispatchToProps與組件上原有的props合並后,通過屬性的方式傳給WrappedComponent
下面進入實戰
1. 創建你的項目, 新建如下文件夾
constants actions reducers store pages
這里說一下 不做異步請求的話 就不用像我那樣用中間件 action 返回函數, 直接return 一個對象, 只有一個 type字段就OK 項目的在 Github 上自己可以看一下
2 npm install react-redux redux redux-logger(這個會打印你的所有 state和 action變化 , 建議使用) redux-thunk(中間件, 用於異步操作) react-navigation(如果你要做多個頁面跳轉就install)
3. 新建一個 Root.js
用 provider 包住你的 APP, 這樣所有的組件就都可以獲取到 store 的 state 變化了 注意要傳遞 store
然后就是把 Root.js 當成 App.js一樣, 放到你的 index.js里即可
4. 接下來就是編寫 action reducer store 了
舉個簡單的加減的同步操作的例子
actionCreactor: 函數返回一個 action
reducer:
這里要提一下, reducer 可以有很多個, 拆分 reducer 對一個不同的操作, 然后用combineReducers 合並所有的 reducer 到 rootReducer, 如下圖:
我個人習慣叫 xxxReducer , 在組件中使用的時候, 可以 state.xxxReducer.xxx 便於區分
5. 在 page里怎么使用
example登錄的操作
下面登錄就這么簡單了只需要一句 this.props.loginAction() , 登錄的邏輯在 loginRequest 里, 發起登錄操作即可, 也算可以給 page解耦,
這里要注意一下 bindActionCreactors 他的作用是把 dispatch 函數無感知的傳遞給子組件, 在子組件里就可以 dispatch 一個 action, 當然你也可以通過 props 把dispatch 函數傳遞給子組件, 只是不優雅, 該函數接受兩個參數, 第一個參數可以是函數或者對象,一般是一個 actionCreactor, 返回的也是一個函數或者對象, 第二個參數是 dispatch,
這里有個 connect 函數 他的作用就是把當前組件和 store 連接, 然后就可以獲取 store 里的 state, store 中的 state 在 dispatch一個 action 之后, 在 reducer 的作用下產生新的 state 返回到 store, 這時你的組件也能接收到新的 state, 不過是以 props 的形式, 如上 connect 函數接受兩個參數, 第一個參數接受兩個參數分別是 mapStateToProps (名字就表示了他的意思, 你需要獲取的 state 會通過props 的形式傳遞到你的當前組件) matchDispatchToProps 這個是你需要的dispatch 的 action , 我更喜歡直接在發生事件的時候去this.props.dispatch(action), 當然這種不規范, 你們還是用matchDispatchToProps 然后就能直接 this.props.loginAction()就 OK 了
這個是那個簡單的同步操作加減的, count就是那個 state, 這里已經通過 mapStateToProps 獲取了, 所以使用的時候就可以this.props.count , 當你 dispatch一個 action 的時候, reducer 會改變他的值, 返回到 store , 最后通過 connect, store 會把這個改變后的 state 更新到這里
當然在其他地方也可以dispatch 這個 action, 這里的 count 也會變化, 當然在其他地方也可以獲取到這個 count, 這就是他的強大之處, 任何地方都能獲取到 store 中存儲的 所有的state, 在任何地方都可以 dispatch 一個你想要 dispatch 的 action, 可以跨組件的操作和數據傳遞提供了很大的便利性和可維護性, 所以這也是他適用於邏輯復雜的應用的原因
下面附上那個 redux-logger 的日志 里面有store 里詳細的 state 變化 以及發起的每一個 action 所以說對於解決 bug 調試起來很省事
是不是一目了然, 你觸發了哪個 action , 改變了什么狀態以及最終的狀態, 傳遞的數據都清晰明了,
總結一下吧, redux 很強大, 也很易用, 下面是一個流程圖
store 就是倉庫
action 就是描述事件發生的對象
reducer 就是事件發生之后如何改變state 的一個純函數