在學react的是,發現一旦我們封裝好了我們的組件,那么我們的項目就跟搭積木一樣簡單快速,可是我們發現了一個問題,在一個頁面往往會嵌套很多的組件,子組件必須要通過父組件傳遞參數才能渲染出數據,我們回想一下我們之前構建過的所有react應用,數據都是由最頂層父組件(頁面組件)一層層向下傳遞的。
這也是深層次的組件之間通訊困難的原因:數據的傳遞是單向的,子組件的數據只能就近獲取,但是真正的數據源卻離得太遠,沒有捷徑可以直接通知數據源更新狀態。
redux的出現改變了react的這種窘迫處境,它提供了整個應用的唯一數據源store,這個數據源是隨處可以訪問的,不需要靠父子相傳,並且還提供了(間接)更新這個數據源的方法,並且是隨處可使用的!
用戶看到的是能是我們的頁面,那么用戶如何跟我們的數據互動呢。首先初始化頁面,我們會渲染store里面的初始數據,
store 是redux提供的唯一數據源,它存儲了整個應用的state,並且提供了獲取state的方法,即
store.getState()。
現在用戶點擊了某個按鈕,那么這個時候用戶就觸發了一個動作,稱之為action,action只是記錄了你剛才"做了什么動作",但是就是知道你干了這個事情,至於事情會發生什么作用,這個並不是你說的算的,就要引入我們的reducer了,如果說action就是你平常做的事,那么reducer就是國家的法律,根據你的事法律會給你相應的后果。
1.我們先看action
action是一個對象,記錄本次事件的類型,以便對應相應的結果,除了type屬性的其他屬性我們稱之為載荷(payload),除了type屬性外,其他屬性都是非必選的。
{ type: 'CHANGE_LIST', data: [1,2,3,4]//這個是假數據,這里當做需要變成的數據,一般是后台拿到的 }
接下來我們要觸發這個事件
2.在redux中觸發這個事件的方式就是:
dispatch({ type: 'CHANGE_LIST', data:[1,2,3,4] })
3.到這里我們觸發了事件,那么事件會產生啥效果呢,這里就提到我們的reducer
假設我們現在有一個列表組件,那么它的數據格式應該可以如下:
// store.getState() { list: { products:['name','age','haha','gege'] } }
現在我們做了步驟2的action操作,那么reducer應該是這樣的:
function reducer(state, action) { switch (action.type) { case 'CHANGE_LIST': return { list: { products:action.data//修改給定的數據 } } default: return state // 沒有匹配的action type,返回原來的state } }
到此為止我們知道這個操作會產生的效果就是原來的數組products從 ['name','age','haha','gege'] 變成了 [1,2,3,4]。
4.拆分reducer
我們在react真實的項目肯定包含許多的組件,不能把所有組件的reducer寫在一個函數里面,這樣會顯得更復雜以及文件特別大,所以我們把上面的reducer改寫一下
function listReducer(state, action) { switch (action.type) { case 'CHANGE_LIST': return { products:action.data//修改給定的數據 } default: return state // 沒有匹配的action type,返回原來的state } } function reducer(state, action) { return { list: listReducer(state.list, action), } }
可以看出達到的效果是一致的,剛好redux為reducer的合並提供了一個簡單的方法combineReducers,
function list(state, action) { switch (action.type) { case 'CHANGE_LIST': return { products:action.data } default: return state // 沒有匹配的action type,返回原來的state } } function dialog(state, action) { switch (action.type) { case 'SHOW_DIALOG': return { status: true } case 'CLOSE_DIALOG': return { status: false } default: return state // 沒有匹配的action type,返回原來的state } } export default combineReducers({ list, dialog })
5.生成store,在我們項目的入口文件app.js中:
import React, { Component, PropTypes } from 'react'
import ReactDom from 'react-dom'
// 引入redux
import { createStore, applyMiddleware } from 'redux'
import { Provider, connect } from 'react-redux'
// 引入reducer
import * as reducers from './redux/reducer.js'
import { combineReducers } from 'redux'
//引入組件
import Index from './component/index.js'
//把多個組件的reducer合成總的reducer
const reducer = combineReducers(reducers)
// 創建store,這個是整個應用唯一的
const store = createStore(reducer)
ReactDom.render(
<Provider store={store}>
<Index />
</Provider>,
document.getElementById('root')
)
然后看看我們的Index組件是個啥樣子:
import React, { Component } from 'react'
import { connect } from 'react-redux'
import List from './list.js'
class Index extends Component {
constructor(props) {
super(props);
}
render () {
return (
<div>
<List />
</div>
)
}
}
function mapStateToProps(state) {
return state //對應本組件需要的傳入的props
}
export default connect(mapStateToProps)(Index)//關聯到store上
下面最重要的看看List組件:
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
class List extends Component {
constructor(props) {
super(props);
}
render() {
var html = this.props.products.map(function(val,i) {
return <li key={i}>{val}</li>
})
return (
<div>
<ul>
{html}
</ul>
<button onClick={this.props.change}>改變數組</button>
</div>
)
}
}
function mapStateToProps(state) {
var info = state.list
return {
products:info.products//對應本組件props需要的屬性products
}
}
function mapDispatchToProps(dispatch) {
return {
change () {//這里change方法對應的是這個組件需要外部傳入的change方法
dispatch({
type: 'CHANGE_LIST',
data:[1,2,3,4]
})
}
})
}
}
}
export default connect(mapStateToProps,mapDispatchToProps)(List)
這里只是簡單介紹了redux基本知識,但是在實際的項目中如何應用呢,實際的項目react和redux結合如何搭建框架呢,這個下篇文章將會列出一個真實的項目。
