一開始接觸redux時對於它和react的關係理解很混亂:
- 一會兒是state,一會兒又是store,這兩者到底有什麼關係?
- 看代碼時,dispatch、reducer、action這三者之間需要跳來跳去,它們到底是什麼關係?爲什麼要這樣設計結構?
- connect、mapStateToProps、mapDispatchToProps這三者有什麼關係?或者說這三個是幹什麼的?跟redux有什麼關係?
- action不就是一個function嗎?它和普通的function有區別嗎?爲什麼它就叫action?它return的值return給誰了?
- redux讓我和react之間隔了一層神祕的面紗,學習成本實在太高了,很多點只是大致知道了,可是就是無法從一個宏觀的角度去理解,結果就是好混亂啊…cry to die…
我們先講一下redux是幹嘛的,它是解決什麼需求的。
先來看看react裏面數據是怎樣傳遞的。
React數據流
- 可以看到,一般都是Container在設置好state後(this.state設置好)
- 把命令一層層地傳遞下去(this.props渲染)
- 要是哪個component想要更新頁面內容了
- 就得一層層往上報告(通過回調),不能越級
- 最好上報到container後,由container去setState修改數據並重新渲染
這個過程是不能越級的,數據傳遞總是讓人覺得不是很靈活。
Redux橫空出世
所以,如果層次不深,組件之間沒有什麼共用數據的話,用react自身的setState其實也可以了。可是,當嵌套一深,或者組件間有一些共用數據時就比較麻煩了,於是就有了redux。
分清兩個state
redux中的state和react中的state完全不是一回事。react中的state是組件內部自己的狀態信息,而redux中的state是redux自己的數據,然後react拿redux的數據來用,其實redux也可以在其他框架下使用,並不是非要跟react一起使用。
簡單畫個圖就像下面這樣
這只是一個簡單的示意圖,實際使用並非如此,這個圖示是爲了讓大家理解react中的數據和readux中的數據是獨立的,並沒有半毛錢關係。
Redux中三個重要角色
網上對於redux教程非常多,這裏簡要介紹一下
1. store
store:創建一個store
import { createStore } from 'redux';
const store = createStore(reducer);
用來讓外界獲取redux數據
store.getState
讓外界修改redux中數據
store.dispatch
2. action
action:描述我要幹啥,一般是一個對象的形式,其中有一個type字段是必須要有的。只有被dispatch的action纔有意義,否則它跟一個普通的function沒什麼區別。
3. reducer
reducer:真正去修改redux的state。
const defaultState = {};
const reducer = (state = defaultState, action) => {
switch (action.type) {
case Type.REQUEST:
return {
...state,
action.request
};
default:
return state;
}
};
我們在平時使用redux時,都是dispatch一個action,而真正去修改state,然後返回一個全新的state的是reducer。
那麼怎麼去觸發一個action呢?
就是前面提到的store.dispatch,把action作爲dispatch的參數。
store.dispatch({ type:Type.Request,test:"100" })
這樣就可以觸發action,執行reducer,返回一個全新的state了。
Redux和React
到此爲止,redux自己就折騰完了,而redux自己的數據並沒有用,它要把數據交給react用才行,接下來講一講怎麼把數據交給react來用。
上面我們創建了一個對象store,我們要把這個store對象作爲props傳給react,那react就可以用了。
這個store只能有一個,也就只能創建一次,也就是說你在最頂層處創建一個store對象,然後再一層一層地傳下去,才能讓所有組件都能獲取這個store對象,調用它的方法。
獲取redux中的數據
比如說我要在render函數中顯示redux數據,那麼我就可以先獲取它的數據:
store.getState()
然後把這個數據當做props渲染到組件中就行了。
更新redux中的數據
如果要修改它的數據,那就在JSX中調用
store.dispatch({type:Type.Request,test:"100})
相應redux中的變化
那麼這裏問題又來了,你調用了store.dispatch之後redux中數據確實變了,可是react並沒有什麼改變啊。也就是說react中的render函數並沒有被觸發,就好像react中你直接修改react中的state是沒用的,而必須調用react中的setState才能重新渲染。
因此,爲了讓redux的數據一改變我們就重新渲染,redux提供了一個監聽方法
store.subscribe(render)
這個函數就可以監聽redux中state的變化,一旦redux中的state發生了變化,render函數就會被調用,頁面就會被重新渲染。
上面這個過程就是手動調用的過程,但這樣調用有點麻煩,因爲要讓所有的組件都能應用store中的數據,那麼所有組件都要把store當做props傳進來,這樣太麻煩了。
React-Redux
這個時候就需要去安裝一些中間件了,還記得你在你的react-native項目中接入redux時安裝的三個庫嗎?
npm install –save redux
npm install –save react-redux
npm install –save redux-thunk
npm install –save react-navigation
其中,react-redux就是爲了簡化redux的調用流程(後面我們會一一介紹這幾個庫都做了什麼,爲什麼要引入這幾個庫)。
在react-redux中有兩個比較關鍵的概念:Provider和connect方法。
Provider
一般我們都將頂層組件包裹在Provider組件之中,這樣的話,所有組件就都可以在react-redux的控制之下了,但是store必須作爲參數放到Provider組件中去
<Provider store = {store}>
<Container/>
<Provider>
這個組件的目的是讓所有組件都能夠訪問到redux中的數據。
這個比較簡單,我們來講connect方法。
connect方法
connect(mapStateToProps,mapDispatchToProps)(MyComponent)
這兩個參數非常重要,我們來講下。
mapStateToProps
字面含義就是把state映射到props中去,意思就是把redux中的數據映射到react中的props中去。
比如一個component想要把網絡請求的數據拿來渲染,就可以在這個component中把redux中的response拿過來用
const mapStateToProps = (state) => {
return {
data: state.response
}
}
然後渲染的時候就可以直接使用this.props.data
class Container extends Component {
constructor(props){
super(props);
}
render(){
return(
<div>this.props.data</div>
)
}
}
const mapStateToProps = (state) => {
return {
data: state.response
}
}
export default connect(mapStateToProps)(Container)
這樣就可以實現渲染,把redux中的state變成react中的props。
mapDispatchToProps
通過上面的分析,相信這個函數也很好理解,就是把各種dispatch變成props直接使用。
const mapDispatchToProps = (dispatch) => {
return {
onClick: () => {
dispatch({
type: Type.REQUEST,
test : "100"
});
}
};
}
更改一下Container組件
class Container extends Component {
constructor(props){
super(props);
}
render(){
return(
<div>this.props.data</div>
<button onClick = {this.props.onClick}>點擊</button>
)
}
}
const mapStateToProps = (state) => {
return {
data: state.data
}
}
const mapDispatchToProps = (dispatch) => {
return {
onClick: () => {
dispatch({
type: Type.REQUEST,
data : "100"
});
}
};
}
export default connect(mapStateToProps,mapDispatchToProps)(Container);
這樣,當點擊按鈕後,Container組件就會自動更新,而不需要我們手動去store.subscribe訂閱render函數以達到更新頁面的目的,這樣一來我們就不需要一層層傳遞store對象了。
tips
這樣隨處都可以使用、修改redux中的數據的方式很方便,但redux推薦的最佳實踐還是在儘可能少的地方使用connect,把邏輯數據相關的都放到容器組件中去處理,把其他的組件都由容器組件所生成的props一層層傳遞下去然後渲染。
redux-thunk
redux-thunk是一個比較流行的redux異步action中間件,比如action中有setTimeOut或fetch,那麼就應該用redux-thunk。這裏就不詳細介紹了。
最後
本文適合一些已經對redux有過一定研究,但思路還是比較混亂的小夥伴,這篇文章一定能幫你理清思路。另外歡迎下載集成了redux的react-native的demo。