Redux 和 React-redux 並非同一個東西。react
Redux 是一種架構模式(Flux 架構的一種變種),它不關注你到底用什麼庫,你能夠把它應用到 React 和 Vue,甚至跟 jQuery 結合都沒有問題。redux
而 React-redux 就是把 Redux 這種架構模式和 React.js 結合起來的一個庫,就是 Redux 架構在 React.js 中的體現。bash
模塊(組件)之間須要共享數據,可是每一個人均可以修改它,一個能夠被不一樣模塊任意修改共享的數據狀態就是魔鬼,一旦數據能夠任意修改,全部對共享狀態的操做都是不可預料的,數據可能被任意修改致使不可預料的結果。架構
咱們定義一個函數,叫 dispatch,它專門負責數據的修改:app
function dispatch (action) {
switch (action.type) {
case 'UPDATE_TITLE_TEXT':
appState.title.text = action.text
break
case 'UPDATE_TITLE_COLOR':
appState.title.color = action.color
break
default:
break
}
}
複製代碼
全部對數據的操做必須經過 dispatch 函數。它接受一個參數 action,這個 action 是一個普通的 JavaScript 對象,裏面必須包含一個 type 字段來聲明你到底想幹什麼。異步
const store = createStore(appState, stateChanger)
store.subscribe(() => renderApp(store.getState())) // 監聽數據變化
renderApp(store.getState()) // 首次渲染頁面
store.dispatch({ type: 'UPDATE_TITLE_TEXT', text: '《React.js 小書》' }) // 修改標題文本
store.dispatch({ type: 'UPDATE_TITLE_COLOR', color: 'blue' }) // 修改標題顏色
複製代碼
經過 store.getState 咱們獲取共享狀態,並且咱們約定只能經過 store.dispatch 修改共享狀態。store 也容許咱們經過 store.subscribe 監聽數據數據狀態被修改了,而且進行後續的例如從新渲染頁面的操做。ide
createStore 接受一個叫 reducer 的函數做爲參數,這個函數規定是一個純函數,它接受兩個參數,一個是 state,一個是 action。函數
reducer 是不容許有反作用的。你不能在裏面操做 DOM,也不能發 Ajax 請求,更不能直接修改 state,它要作的僅僅是 —— 負責初始 state,和根據 state 和 action 計算具備共享結構的新的 state。學習
const reducer = (state = {count: 0}, action) => {
switch (action.type){
case 'INCREASE': return {count: state.count + 1};
case 'DECREASE': return {count: state.count - 1};
default: return state;
}
}
const actions = {
increase: () => ({type: 'INCREASE'}),
decrease: () => ({type: 'DECREASE'})
}
const store = createStore(reducer);
store.subscribe(() =>
console.log(store.getState())
);
store.dispatch(actions.increase()) // {count: 1}
store.dispatch(actions.increase()) // {count: 2}
store.dispatch(actions.increase()) // {count: 3}
複製代碼
經過 reducer 建立一個 store,每當咱們在 store 上 dispatch 一個 action,store 內的數據就會相應地發生變化。ui
建立store的時候,要先建立reducer,把reducer傳遞進store下的index.js reducer裏放的就是存放的數據
reducer能夠接收state,可是毫不能修改state
一、store必須是惟一的
二、只有store可以改變本身的內容,reducer不能改
三、reducer必須是純函數(給定固定的輸入,就必定會有給定的輸出,return的值是必定的)即不能有異步操做,也不能有關於時間定時器之類的操做
store.dispatch:派發action,幫助傳遞給store
store.getState:獲取store中的數據
store.subscribe:監聽數據的改變,觸發監聽回調
首先在最外層容器中,把全部內容包裹在 Provider 組件中,將以前建立的 store 做爲 prop 傳給 Provider。
const App = () => {
return (
<Provider store={store}>
<Comp/>
</Provider>
)
};
複製代碼
Provider 內的任何一個組件(好比這裏的 Comp),若是須要使用 state 中的數據,就必須是「被 connect 過的」組件——使用 connect 方法對「你編寫的組件(MyComp)」進行包裝後的產物。
class MyComp extends Component {
// content...
}
const Comp = connect(...args)(MyComp);
複製代碼
可見,connect 方法是重中之重。
connect() 接收四個參數,它們分別是 mapStateToProps,mapDispatchToProps,mergeProps和options。
這個函數容許咱們將 store 中的數據做爲 props 綁定到組件上。
const mapStateToProps = (state) => {
return {
count: state.count
}
}
複製代碼
這個函數的第一個參數就是 Redux 的 store,咱們從中摘取了 count 屬性。由於返回了具備 count 屬性的對象,因此 MyComp 會有名爲 count 的 props 字段。
class MyComp extends Component {
render(){
return <div>計數:{this.props.count}次</div>
}
}
const Comp = connect(...args)(MyComp);
複製代碼
固然,你沒必要將 state 中的數據原封不動地傳入組件,能夠根據 state 中的數據,動態地輸出組件須要的(最小)屬性。
const mapStateToProps = (state) => {
return {
greaterThanFive: state.count > 5
}
}
複製代碼
函數的第二個參數 ownProps,是 MyComp 本身的 props。有的時候,ownProps 也會對其產生影響。好比,當你在 store 中維護了一個用戶列表,而你的組件 MyComp 只關心一個用戶(經過 props 中的 userId 體現)。
const mapStateToProps = (state, ownProps) => {
// state 是 {userList: [{id: 0, name: '王二'}]}
return {
user: _.find(state.userList, {id: ownProps.userId})
}
}
class MyComp extends Component {
static PropTypes = {
userId: PropTypes.string.isRequired,
user: PropTypes.object
};
render(){
return <div>用戶名:{this.props.user.name}</div>
}
}
const Comp = connect(mapStateToProps)(MyComp);
複製代碼
當 state 變化,或者 ownProps 變化的時候,mapStateToProps 都會被調用,計算出一個新的 stateProps,(在與 ownProps merge 後)更新給 MyComp。
這就是將 Redux store 中的數據鏈接到組件的基本方式。
connect 的第二個參數是 mapDispatchToProps,它的功能是,將 action 做爲 props 綁定到 MyComp 上。
const mapDispatchToProps = (dispatch, ownProps) => {
return {
increase: (...args) => dispatch(actions.increase(...args)),
decrease: (...args) => dispatch(actions.decrease(...args))
}
}
class MyComp extends Component {
render(){
const {count, increase, decrease} = this.props;
return (<div>
<div>計數:{this.props.count}次</div>
<button onClick={increase}>增長</button>
<button onClick={decrease}>減小</button>
</div>)
}
}
const Comp = connect(mapStateToProps, mapDispatchToProps)(MyComp);
複製代碼
因爲 mapDispatchToProps 方法返回了具備 increase 屬性和 decrease 屬性的對象,這兩個屬性也會成爲 MyComp 的 props。
如上所示,調用 actions.increase() 只能獲得一個 action 對象 {type:'INCREASE'},要觸發這個 action 必須在 store 上調用 dispatch 方法。diapatch 正是 mapDispatchToProps 的第一個參數。可是,爲了避免讓 MyComp 組件感知到 dispatch 的存在,咱們須要將 increase 和 decrease 兩個函數包裝一下,使之成爲直接可被調用的函數(即,調用該方法就會觸發 dispatch)。
Redux 自己提供了 bindActionCreators 函數,來將 action 包裝成直接可被調用的函數。
import {bindActionCreators} from 'redux';
const mapDispatchToProps = (dispatch, ownProps) => {
return bindActionCreators({
increase: action.increase,
decrease: action.decrease
});
}
複製代碼
一樣,當 ownProps 變化的時候,該函數也會被調用,生成一個新的 dispatchProps,(在與 statePrope 和 ownProps merge 後)更新給 MyComp。注意,action 的變化不會引發上述過程,默認 action 在組件的生命週期中是固定的。
以前說過,不論是 stateProps 仍是 dispatchProps,都須要和 ownProps merge 以後纔會被賦給 MyComp。connect 的第三個參數就是用來作這件事。一般狀況下,你能夠不傳這個參數,connect 就會使用 Object.assign 替代該方法。