Store
。組件每次從新渲染,都必須由狀態變化引發。action
。reducer
函數接收action
,而後根據當前的state
,計算出新的state
。state
(狀態)。經過限制更新發生的時間和方式,Redux 試圖讓 state
的變化變得可預測。{ type: 'LIKE_ARTICLE', articleId: 42 };
{ type: 'FETCH_USER_SUCCESS', response: { id: 3, name: 'Mary' } };
{ type: 'ADD_TODO', text: 'Read the Redux docs.'};複製代碼
action
理解成新聞的摘要。如 「瑪麗喜歡42號文章。」 或者 「任務列表裏添加了'學習 Redux 文檔'」。store.dispatch(action)
,包括組件中、XHR 回調中、甚至定時器中。reducer
: 當前的 state
樹和 action
。例如,在這個 todo
應用中,根 reducer
可能接收這樣的數據:// 當前應用的 state(todos 列表和選中的過濾器)
let previousState = {
visibleTodoFilter: 'SHOW_ALL',
todos: [
{
text: 'Read the docs.',
complete: false
}
]
}
// 將要執行的 action(添加一個 todo)
let action = {
type: 'ADD_TODO',
text: 'Understand the flow.'
}
// render 返回處理後的應用狀態
let nextState = todoApp(previousState, action);複製代碼
dispatch(action)
前發生。reducer
的結構徹底由你決定。Redux 原生提供combineReducers()
輔助函數,來把根 reducer
拆分紅多個函數,用於分別處理 state
樹的一個分支。-下面演示 combineReducers() 如何使用。假如你有兩個 reducer:一個是 todo 列表,另外一個是當前選擇的過濾器設置:javascript
function todos(state = [], action) {
// 省略處理邏輯...
return nextState;
}
function visibleTodoFilter(state = 'SHOW_ALL', action) {
// 省略處理邏輯...
return nextState;
}
let todoApp = combineReducers({
todos,
visibleTodoFilter
})複製代碼
action
後,combineReducers
返回的 todoApp
會負責調用兩個 reducer:let nextTodos = todos(state.todos, action);
let nextVisibleTodoFilter = visibleTodoFilter(state.visibleTodoFilter, action);複製代碼
return {
todos: nextTodos,
visibleTodoFilter: nextVisibleTodoFilter
};複製代碼
combineReducers()
是一個很方便的輔助工具,你也能夠選擇不用;你能夠自行實現本身的根 reducer
!這個新的樹就是應用的下一個 state!全部訂閱 store.subscribe(listener) 的監聽器都將被調用;監聽器裏能夠調用 store.getState() 得到當前 state。java
如今,能夠應用新的 state
來更新 UI。若是你使用了 React Redux 這類的綁定庫,這時就應該調用 component.setState(newState)
來更新。react
state
被儲存在一棵 object tree
中,而且這個 object tree
只存在於惟一一個 store
中。 state
的方法就是觸發 action
,action
是一個用於描述已發生事件的普通對象。action
如何改變 state tree
,你須要編寫 reducers
。store
,這個store
有Redux提供的createStore(reducers,[initialState])
方法生成。從函數簽名看出,想生成store
,必須傳入reducers
,同時也能夠傳入第二個可選參數初始化狀態initialState
。使用方法 const store = createStore(reducer);
redux
action
並修改數據的角色就是reducer
。reducer
本質上是一個純函數,其函數簽名爲reducer(previousState, action) => newState
。reducer
在處理action
時,需傳入一個previousState
參數。reducer
的職責就是根據previousState
和action
來計算出新的newState
。使用方法將reducer
即下面的todo
做爲參數傳入createStore(todo)中數組
//如下爲reducer的格式
const todo = (state = initialState, action) => {
switch(action.type) {
case 'XXX':
return //具體的業務邏輯;
case 'XXX':
return //具體的業務邏輯;
default:
return state;
}
}複製代碼
getState()
store
中的狀態。store.dispatch(action)
action
,並返回這個action
,這是惟一能改變store
中數據的方式。store.dispatch接受一個Action對象做爲參數,將它發送出去。store.subscribe(listenter)
store
發生變化時被調用,一旦State發生了變化,就會自動執行這個函數。經過subscribe綁定了一個監聽函數以後,只要dispatch了一個action,全部監聽函數都會自動執行一遍。store
裏的reducer
,通常只會在開發者模式中調用該方法。包含getState()
,dispatch(action)
,subscribe(listener)
;本函數近似源碼,可簡單實現功能與幫助理解createStore
的原理app
const createStore = (reducer) => {
let state; //聲明一個變量承接狀態
let list = [];//聲明一個數組用於儲存監聽函數
const getState = () => {
return state;//直接返回state;
}
const dispatch = (action) =>{
state = reducer(state, action);//更新狀態,且循環list數組,並執行裏面的事件
list.forEach((fn) => {
fn();
})
}
const subscribe = (fn) => {
list.push(fn);//將函數傳入list中
return () => {
list = list.filter(cd => cd != fn)
}
}
return {
getState,
subscribe,
dispatch
}
}複製代碼
reducer
函數 進行拆分,拆分後的每一塊獨立負責管理 state
的一部分。combineReducers
輔助函數的做用是,把一個由多個不一樣 reducer
函數做爲 value
的 object
,合併成一個最終的 reducer
函數,而後就能夠對這個 reducer
調用 createStore
。reducer
做爲對象的屬性傳入combineReducers({})
函數中,const rootReducer = combineReducers({
reducer1,
reducer2,
...
})複製代碼
(Function):一個調用 reducers 對象裏全部 reducer 的 reducer,而且構造一個與 reducers 對象結構相同的 state 對象。ide
let store = createStore(rootReducer)
//store = {
reducer1: ... ,
reducer2: ... ,
...
}
let {reducer1, reducer2} = store; //取出複製代碼
//研究邏輯看這個
const combineReducers = (reducers) => {
return (state = {}, action) => {
let newState = {};
Object.keys(reducers).forEach((key) =>{
newState[key] = reducers[key](state[key], action);
})
return newState;
}
}
//簡寫裝逼看這個
const combineReducers = (reducers) => (state = {}, action) => Object.keys(reducers).reduce((newState, key) => {
newState[key] = reducers[key](state[key], action);
return newState;
},{})複製代碼
prop
一層層傳遞下去固然是沒問題的。不過這也太麻煩啦,要是能在最外層和最裏層之間開一個穿越空間的蟲洞就行了。Context
。注意:context
一直都在React源碼中,但在React0.14版本才被記錄官方文檔。官方並不太推薦大量使用,雖然它能夠減小逐級傳遞,但當組件複雜時,咱們並不知道context
是從哪傳來的。它就相似於全局變量。函數
在外層定義一個getChildContext
方法,在父層制定childContextTypes
。工具
class Provider extends Component{
getChildContext() {
return {store: ...};
}
render(){
return(
this.props.children
)
}
}
Provider.childContextTypes = {
store : React.PropTypes.object
};複製代碼
在內層設置組件的contextTypes
後,便可在組件裏經過this.context.
來訪問。學習
class child extends Component{
render(){
const store = this.context.store;
return(
<div>1</div>
)
}
}
child.contextTypes = {
store: React.PropTypes.object
}複製代碼
createStore()
方法,這樣還不足以讓Redux在咱們的react應用中發揮做用,還須要react-redux庫 ———— Redux官方提供的React綁定。<Provider />
, 一個是connect()
。關於它們,咱們須要知道的是,<Provider />
接受一個store
做爲props
,它是整個Redux應用的頂層組件,而connect()
提供了在整個React應用的任意組件中獲取store
中數據的功能。其實就是建立一個外層包裹住整個Redux應用。
<Provider />
主要源碼
export default class Provider extends Component {
getChildContext() {
return { store: this.store }
}
constructor(props, context) {
super(props, context)
this.store = props.store
}
render() {
return Children.only(this.props.children)
}
}複製代碼
ReactDom.render(
<Provider store = {store}> <App /> </Provider>,
document.getElementById("root")
)複製代碼
使用方法connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])(TodoApp)
上面代碼看似那麼長,但其實理解起來不太難,前四個參數是選填屬性,根據需求填入便可。connect(...)
調用後會返回一個函數這個函數可傳一個參數,即你須要綁定的組件。
connect
函數,只針對前兩個關鍵參數。const connect = (mapStateToProps, mapDispatchToProps) => {
return (WrapperComponent) => {
class Connect extends Component {
componentDidMount() {
const store = this.context.store;
this.unsubscribe = store.subscribe(() => {
this.forceUpdate();
})
}
componentWillUnmount() {
this.unsubscribe();
}
render (){
const store = this.context.store;
const stateProps = mapStateToProps(store.getState());
const dispatchProps = mapDispatchToProps(store.dispatch);
const props = Object.assign({}, stateProps, dispatchProps);
// return <WrapperComponent {...props} />;
return React.createElement(WrapperComponent, props);
}
}
Connect.contextTypes = {
store: React.PropTypes.object
};
return Connect;
}
}複製代碼
官方解釋: 若是定義該參數,組件將會監聽 Redux store
的變化。任什麼時候候,只要 Redux store
發生改變,mapStateToProps
函數就會被調用。該回調函數必須返回一個純對象,這個對象會與組件的 props
合併。若是你省略了這個參數,你的組件將不會監聽 Redux store
。若是指定了該回調函數中的第二個參數 ownProps
,則該參數的值爲傳遞到組件的 props
,並且只要組件接收到新的 props
,mapStateToProps
也會被調用。
使用方法(其實裏面第一個參數就是最先在 <Provider store = {store}>
傳入的store
,因而能夠在子組件上訪問store
裏的屬性)
const mapStateToProps = (state, [ownProps]) => {
return {
todos : state.todos
}
}複製代碼
action creator
,並且這個對象會與 Redux store
綁定在一塊兒,其中所定義的方法名將做爲屬性名,合併到組件的 props
中。若是傳遞的是一個函數,該函數將接收一個 dispatch
函數,而後由你來決定如何返回一個對象,這個對象經過 dispatch
函數與 action creator
以某種方式綁定在一塊兒(提示:你也許會用到 Redux 的輔助函數 bindActionCreators())。若是你省略這個 mapDispatchToProps
參數,默認狀況下,dispatch
會注入到你的組件 props
中。若是指定了該回調函數中第二個參數 ownProps
,該參數的值爲傳遞到組件的 props
,並且只要組件接收到新 props
,mapDispatchToProps
也會被調用。mapStateToProps
是用來傳遞屬性狀態的,而mapDispatchToProps
是用來傳遞改變的方法的。const mapDispatchToProps = (dispatch, [ownProps]) => {
return{
... : () => {
dispatch(...)
}
}
}複製代碼
mergeProps(stateProps, dispatchProps, ownProps)
能夠接受stateProps, dispatchProps, ownProps三個參數。stateProps
就是傳給connect
的第一個參數mapStateToProps
最終返回的props
。dispatchProps
就是傳給connect
的第二個參數mapDispatchToProps
最終返回的props
。ownProps
則爲組件本身的props
。若是指定這個參數,能夠定製 connector 的行爲。
[pure = true] (Boolean): 若是爲 true
,connector
將執行 shouldComponentUpdate
而且淺對比 mergeProps
的結果,避免沒必要要的更新,前提是當前組件是一個「純」組件,它不依賴於任何的輸入或 state
而只依賴於 props
和 Redux store
的 state
。默認值爲 true
。
[withRef = false] (Boolean): 若是爲 true
,connector
會保存一個對被包裝組件實例的引用,該引用經過 getWrappedInstance()
方法得到。默認值爲 false
。