衆所周知,React中數據通訊是單向的,即父組件能夠經過props向子組件傳遞數據,而子組件卻不能向父組件傳遞數據。要實現子組件向父組件傳遞數據的需求,須要父組件提供一個修改數據的方法,當頁面愈來愈多的時候,數據的管理就會變得異常複雜。
而且,每次數據的更新都須要調用setState,特別是涉及到跨組件通訊的問題就會很麻煩。在React開發中,爲了解決跨組件通訊的問題,業界開發了一大批狀態管理框架,目前比較經常使用的React狀態管理框架有Flux、Redux和Mobx等幾個。
其中,Flux是Facebook用於創建客戶端Web應用的前端架構,它利用一個單向數據流的方式補充了React的組合視圖組件,解決了MVC技術架構中數據流管理混亂的問題。Redux則是由Dan Abramov開源的一款前端狀態管理框架,Redux框架由Action、Store和Reducers三部分組成,全部組件的數據都存儲到Store對象中,每一個組件只須要改變Store中的數據,當Store數據發生變化時就會其餘訂閱的組件執行數據更新。Mobx是一個面向對象的狀態管理框架,它與Redux的最大區別是能夠直接修改數據,精準的通知UI進行刷新,而不是Redux的廣播。
能夠發現,Redux特別適合用在須要集中式管理數據場景中。多個組件使用同一個數據源,維護同一個數據樣本,進而保持各個組件之間數據的一致性。react-redux是Redux狀態框架在React中的技術實現,對於熟悉Redux狀態管理框架開發者來講,學習react-redux將會顯得很是容易。
在Redux狀態框架中,Redux將狀態管理分爲Action、Store和Reducers三部分。其中,Redux將應用程序的狀態存儲到Store中,組件經過dispatch()方法觸發Action,Store接收Action並將Action轉發給Reducer,Reducer根據Action類型對狀態數據進行處理並將處理結果返回給Store,其餘組件經過訂閱Store狀態的來刷新自身的狀態,整個框架的工做流程如圖3-9所示。前端
下面以計數器爲例來講明Redux的基本使用。首先,建立一個action.js文件,用來存放Action行爲事件,以下所示。react
export const ADD = 'ADD' export const MINUS = 'MINUS'
而後,建立一個reducer.js文件,用來處理業務邏輯的更新,並將處理的結果返回給Store,以下所示。redux
import {ADD, MINUS} from './action'; function reducer (state = {count: 0}, action) { switch(action.type) { case ADD: return {count: state.count + 1} case MINUS: return {count: state.count - 1} default: return state } } export default reducer;
Reducer是一個純函數,接收State和Action兩個參數。其中,State是舊的狀態,不能夠直接修改,Reducer會根據Action的類型來生成不一樣的新State,並將結果返回給Store。
接下來,建立一個全局的Store對象,用來存放應用的狀態數據,建立時須要使用Store提供的createStore()方法,以下所示。架構
import { createStore } from 'redux' import reducer from './reducer'; const store = createStore(reducer) export default store
除了createStore()方法外,建立的Store還有如下幾個方法能夠調用。框架
爲了實現計數器加減的功能,還須要在組件的生命週期函數中添加訂閱事件,並在組件銷燬時解決訂閱,以下所示。ide
class CounterPage extends React.Component { constructor(props){ super(props) this.state = { number: store.getState().count } } componentDidMount () { this.unSubscribe = store.subscribe(() => { this.setState({ number: store.getState().count }) }) } componentWillUnmount () { this.unSubscribe && this.unSubscribe() } render() { return ( <View style={styles.ct}> <Text>{this.state.number}</Text> <Button title="加1" onPress={() => store.dispatch({type: 'ADD'})}/> <Button title="減1" onPress={() => store.dispatch({type: 'MINUS'})}/> </View> ); } } const styles = StyleSheet.create({ ct: { flex: 1, justifyContent: 'center', alignItems: 'center', }, }); export default CounterPage;
在上面的代碼中,咱們經過store.getState()方法來獲取最新的State,而執行加減操做時經過store.dispatch()方法派發Action給Store。能夠發現,在類組件中使用Redux仍是挺繁瑣的,須要開發者本身管理組件的狀態數據,而若是改用React Hook就要簡單許多。
在React Hook中使用Redux須要使用react-redux庫提供的useSelector()與useDispatch()兩個函數。其中,useSelector()函數能夠用來獲取狀態值,而useDispatch()則能夠用來修改狀態數據,以下所示。函數
import { useSelector, useDispatch } from 'react-redux' const CounterPage = () => { const count = useSelector(state => state.count) const dispatch = useDispatch() return ( <View style={styles.ct}> <Text>{count}</Text> <Button title='加1' onPress={() => dispatch({type: 'ADD'})}/> <Button title='減1' onPress={() => dispatch({type: 'MINUS'})}/> </View> ); } const styles = StyleSheet.create({ …. //省略代碼 }); export default CounterPage
能夠發現,相比於類組件來講,使用React Hook實現就要簡潔許多。首先,咱們使用useSelector()函數獲取Store中的狀態,而後再使用useDispatch()函數派發事件。
最後,使用Redux在讓不一樣組件之間共享狀態數據時,還須要使用react-redux庫提供的Provider包裹應用組件,以下所示。學習
const App = () => { return ( <Provider store={store}> <CounterPage /> </Provider> ); };
從新運行代碼,就實現了計數器的功能,以下圖所示。
flex
最後,須要說明的是,使用Redux進行狀態管理時,應注意如下幾點:
• 應用中有且僅有一個Store,該Store存儲了整個應用的狀態。
• State是隻讀的,修改State只能經過派發Action事件,爲了描述Action改變State的過程,須要使用Reducer純函數。
• 單一數據源讓多個React組件之間的通訊更加方便,也有利於狀態的統一管理。this