在使用react的過程當中,用redux來管理應用中的狀態,使應用流更清晰的同時也會有小小的疑惑,好比reducer在redux中時怎麼發揮做用的,爲何只要寫好reducer,和dispatch特定action配合,store的狀態就會更新了,而react組件又是如何和store的狀態產生關係的,爲何隨着store狀態的更新,react組件會被自動更新,下面就從redux的源碼開始來總結下這其中的原因~react
redux是繼承自flux體系,但它放棄了dispatcher,無需使用 event emitters(事件發送器)(在dispatcher中特定action邏輯裏觸發事件,組件裏監聽事件),而使用純 reducer來代替,那麼reducer是如何被調度的,又是如何影響狀態更新的,不妨經過源碼的邏輯來了解和加深一下~redux
reducer一般是由咱們本身來寫,在調用createStore
函數生成store時來傳入這個reducer
,後續調用store
的dispatch
方法來觸發action
時,則reducer
函數會自動被調用來解析actin
更新state
,這一切的核心都在crateStore
方法中數組
export default function createStore(reducer, preloadedState, enhancer) {
...
let currentReducer = reducer
let currentState = preloadedState
let currentListeners = []
let nextListeners = currentListeners
let isDispatching = false
function getState() {
...
}
function subscribe(listener) {
...
}
function dispatch(action) {
...
}
function replaceReducer(nextReducer) {
...
}
function observable() {
...
}
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
}
複製代碼
能夠看到createStore方法返回一個屬性集合,咱們所調用的redux的相關方法都是定義在createStroe方法內部,最後被經過這個屬性集合中暴露出來,如處理action的dispath方法,同currentReducer,currentState是createStore方法中的私有變量,由dispath,subscribe,getState等方法共享,咱們設置的reducer,redux的state狀態,以及state改變以後應該自動觸發哪些函數,這些邏輯都是經過這幾個內部變量和函數來實現的,下面先來看一下幾個核心方法,由咱們直接接觸到的dispath開始~bash
function dispatch(action) {
...
try {
//將flag置爲true,代表處於分發邏輯中
isDispatching = true
//currentReducer即爲函入的reducer函數,這裏會自動調用currentReducer函數,並將返回值賦給currentState
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
//調用監聽函數
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
return action
}
複製代碼
能夠看到,在dispath中調用reducer方法處理action以後,返回值(新的state)會直接賦值給currentState
,由此能夠推測currentState
應該就是getState
要返回的狀態ide
function getState() {
if (isDispatching) {
throw new Error(
...
)
}
return currentState //直接返回currentState內部變量
}
複製代碼
function subscribe(listener) {
if (typeof listener !== 'function') {
throw new Error(...)
}
if (isDispatching) {
throw new Error(
...
)
}
let isSubscribed = true
...
nextListeners.push(listener)
return function unsubscribe() {
...
}
複製代碼
在subscribe中,傳入的listener函數會被添加進nextListeners數組中,當dispatch方法被調用時自動觸發,react-redux的狀態更新時,UI自動更新的特性是經過subscribe來實現的函數
首先,設想咱們不知道react-redux庫來鏈接react和redux,來試一下單純的經過redux做爲react組件的狀態管理器ui
//首先,建立一個reducer處理函數
function todoApp(state = initialState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return ...
case ADD_TODO:
return ...
default:
return state
}
}
//建立store
const store = createStore(
todoApp
);
複製代碼
要在各組件之間共享變量store有兩種方式可取,一種是經過props共享,一種是經過context實現,其中props共享變量須要每一層組件之間層層傳遞該變量,這樣作無疑很麻煩,尤爲是組件以前嵌套層次比較深的時候,因此咱們這裏用react的context屬性來實現共享storethis
class App extends Component {
getChildContext() {
return {
store: this.props.store
};
}
render() {
return <ToDoList />;
}
}
App.childContextTypes = {
store: React.PropTypes.object
}
複製代碼
class VisibleTodoList extends Component {
componentDidMount() {
const { store } = this.context;
this.unsubscribe = store.subscribe(() =>
this.forceUpdate()
);
}
render() {
const props = this.props;
const { store } = this.context;
const state = store.getState();
// ...
}
}
VisibleTodoList.contextTypes = {
store: React.PropTypes.object
}
複製代碼
如上所示,在須要獲取store狀態的組件中,在組件加載完成後須要獲取關心得context的變量值store,同時訂閱事件,當store的狀態變化後觸發組件自己的強制更新,而render中只需用store.getState
獲取store的狀態值便可spa
上例中寫了一個組件的實現還好,但當組件多的時候,每一個組件都需寫本身的獲取context,訂閱事件強制更新自身,獲取state,這樣的樣板代碼,實際是不必的,徹底能夠把這部分抽象出來,而react-redux就幫咱們作了這些,讓咱們省去了自定義context和訂閱事件,獲取state等操做code
要利用redux
來管理狀態,須要在祖先組件的context
屬性中指定store
,而這必定式化的操做能夠有react-redux
庫中的Provider來完成
,示例以下
<Provider store={store}>
<App />
</Provider>
複製代碼
上節中提到過,要實現store的狀態更新後能自動更新react組件,則組件需在掛載後調用store
的subscribe
方法來訂閱store
中狀態的變動,而這塊兒樣板代碼則能夠由react-redux
庫中的connect
建立的容器組件來自動完成
class TodoList extends Component {
render(){
...
}
}
const mapStateToProps = state => {
return {
todos: getVisibleTodos(state.todos, state.visibilityFilter)
}
}
const mapDispatchToProps = dispatch => {
return {
onTodoClick: id => {
dispatch(toggleTodo(id))
}
}
}
const VisibleTodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
複製代碼
其中mapStateToProps提供從Redux store state到展現組件的 props的映射 ,mapDispatchToProps接收dispatch方法並提供指望注入到展現組件的 props 中的回調方法。