深刻理解redux之從redux源碼到react-redux的原理

在使用react的過程當中,用redux來管理應用中的狀態,使應用流更清晰的同時也會有小小的疑惑,好比reducer在redux中時怎麼發揮做用的,爲何只要寫好reducer,和dispatch特定action配合,store的狀態就會更新了,而react組件又是如何和store的狀態產生關係的,爲何隨着store狀態的更新,react組件會被自動更新,下面就從redux的源碼開始來總結下這其中的原因~react

redux管理狀態的原理

redux是繼承自flux體系,但它放棄了dispatcher,無需使用 event emitters(事件發送器)(在dispatcher中特定action邏輯裏觸發事件,組件裏監聽事件),而使用純 reducer來代替,那麼reducer是如何被調度的,又是如何影響狀態更新的,不妨經過源碼的邏輯來了解和加深一下~redux

從redux源碼看action,reducer,和store之間的關係

reducer一般是由咱們本身來寫,在調用createStore函數生成store時來傳入這個reducer,後續調用storedispatch方法來觸發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

dispacth處理action

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

getState返回當前的狀態

function getState() {
    if (isDispatching) {
      throw new Error(
      ...
      )
    }
    return currentState //直接返回currentState內部變量
  }
複製代碼

subscribe訂閱狀態

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來實現的函數

經過redux來管理react的狀態

首先,設想咱們不知道react-redux庫來鏈接react和redux,來試一下單純的經過redux做爲react組件的狀態管理器ui

建立store

//首先,建立一個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的狀態

要在各組件之間共享變量store有兩種方式可取,一種是經過props共享,一種是經過context實現,其中props共享變量須要每一層組件之間層層傳遞該變量,這樣作無疑很麻煩,尤爲是組件以前嵌套層次比較深的時候,因此咱們這裏用react的context屬性來實現共享storethis

根組件提供context

class App extends Component {
  getChildContext() {
    return {
      store: this.props.store
    };
  }
  render() {
    return <ToDoList />;
  }
}

App.childContextTypes = {
  store: React.PropTypes.object
}
複製代碼

子組件獲取context

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

用react-redux來簡化組件的寫法

上例中寫了一個組件的實現還好,但當組件多的時候,每一個組件都需寫本身的獲取context,訂閱事件強制更新自身,獲取state,這樣的樣板代碼,實際是不必的,徹底能夠把這部分抽象出來,而react-redux就幫咱們作了這些,讓咱們省去了自定義context和訂閱事件,獲取state等操做code

省去自定義含有context屬性的根組件

要利用redux來管理狀態,須要在祖先組件的context屬性中指定store,而這必定式化的操做能夠有react-redux庫中的Provider來完成,示例以下

<Provider store={store}>
    <App />
  </Provider>
複製代碼

省去手動訂閱store的狀態變化事件

上節中提到過,要實現store的狀態更新後能自動更新react組件,則組件需在掛載後調用storesubscribe方法來訂閱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 中的回調方法。

相關文章
相關標籤/搜索