揭開redux,react-redux的神祕面紗

蘇溪雲發表於2019-03-03

16年開始使用react-redux,迄今也已兩年多。這時候再來閱讀和讀懂redux/react-redux原始碼,雖已沒有當初的新鮮感,但依然覺得略有收穫。把要點簡單寫下來,一方面供感興趣的讀者參考,另一方面也是自己做下總結。

redux

react-redux最核心的內容就是redux。內帶redux,react-redux只提供了幾個API來關聯redux與react的元件以及react state的更新。

首先,看下如何使用redux。 redux老司機可以直接滑動滾輪至下一章。
簡單來說,redux有三個概念,action, reducer 和 dispatch。 action和dispatch比較好理解:動作指令和提交動作指令方法。而reducer,個人在字面上沒有理解,但抽象層面上可以理解為用來生成state的函式。用一個簡單案例體現這三個概念:

// action
const INCREMENT = { type: `INCREMENT` }

// reducer
function count( state = 0, action ) {
    switch( action.type ) {
        case `INCREMENT`:
            return state + 1
        default: 
          return state
    }
}

// dispatch
// 此處開始使用redux
const store = redux.createStore( count )
console.log( store.getState() )  // 0
store.dispatch( INCREMENT )
console.log( store.getState() ) // 1
複製程式碼

接下來說說redux中的兩大模組:

  • store物件
  • 中介軟體

store物件

APIcreateStore會建立了一個store物件,建立的過程中它主要做了下面兩件事:

  1. 初始化state
  2. 暴露相關介面:getState(), dispatch( action ), subscribe( listener )等。其中getState()用來獲取store中的實時state, dispatch(action)根據傳入的action更新state, subscribe( listener)可以監聽state的變化。

中介軟體

中介軟體可以用來debug或提交非同步動作指令. 在初始化store的時候,我們通過createStore( reducer, state, applyMiddleware( middleware1, middleware2 ) )新增多箇中介軟體。
為了實現多箇中介軟體,redux專門引入了函數語言程式設計的compose()方法,簡單來說,compose將多層函式呼叫的寫法變得優雅:

// 未使用compose方法
a( b( c( `d` ) ) )

// 用compose方法
compose( a, b, c )(`d`)
複製程式碼

而中介軟體的寫法比較奇特,是多級函式,在閱讀原始碼的時候有點繞。顯然中介軟體的寫法還可以優化,儘管現在的寫法方便在原始碼中使用,但對redux使用者來說稍顯複雜,可以用單層函式。

function logMiddleware({ getState  }) {
    return nextDispatch => action => {
        console.log( `before dispatch`, getState() )
        const res = nextDispatch( action )
        console.log( `after dispatch`, getState() )
        return res
    }
}
複製程式碼

react-redux

瞭解了redux運作原理,就可以知道react-redux的大部分使用場景是如何運作。react-redux提供了幾個API將redux與react相互關聯。

基於上一個案例展示react-redux的用法:

// action
const increment = () => ({ type: `INCREMENT` })

// reducer
function count( state = 0, action ) {
    switch( action.type ) {
        case `INCREMENT`:
            return state + 1
        default: 
          return state
    }
}

// redux
const store = Redux.createStore( count )

// react-redux
const { Provider, connect } = ReactRedux
const mapStateToProps = state => ( { count: state } )
const mapDispatchToProps = dispatch => ( { increment : () => dispatch( increment() ) } )
const App = connect( mapStateToProps, mapDispatchToProps )( class extends React.Component {
  onClick = () => {
   this.props.increment()
  }  
  render() {
        return <div>
          <p>Count: { this.props.count }</p>
          <button onClick={ this.onClick }>+</button>
        </div>
    }
} )

ReactDOM.render( <Provider store={ store }>
    <App />
</Provider>, document.getElementById( `app` ) )
複製程式碼

點選執行案例

react-redux提供最常用的兩個API是:

  • Provider
  • connect

Provider

Provider本質上是一個react元件,通過react的context api(使一個元件可以跨多級元件傳遞props)掛載redux store中的state,並且當元件初始化後開始監聽state。
當監聽到state改變,Provider會重新setState在context上的storeState,簡要實現程式碼如下:

class Provider extends Component {
    constructor(props) {
      super(props)
    
      const { store } = props
    
      this.state = {
        storeState: Redux.store.getState(),
      }
    }
    
    componentDidMount() {
      this.subscribe()
    }
    
    subscribe() {
        const { store } = this.props
        store.subscribe(() => {
          const newStoreState = store.getState()
          this.setState(providerState => {
            return { storeState: newStoreState }
          })
        })
    }
    render() {
        const Context = React.createContext(null)
        <Context.Provider value={this.state}>
            {this.props.children}
        </Context.Provider>
    }
}
複製程式碼

connect()

connect方法通過connectHOC(HOC: react高階元件)將部分或所有state以及提交動作指令方法賦值給react元件的props。

小結

寫react不用redux就像寫程式碼不用git, 我們需要用redux來更好地管理react應用中的state。瞭解redux/react-redux的運作原理會消除我們在使用redux開發時的未知和疑惑,並且在腦中有一個完整的程式碼執行迴路,讓開發流程變得透明,直觀。

如果本文幫助到了你,我也十分榮幸, 歡迎點贊和收藏。如果有任何疑問或者建議,都歡迎在下方評論區提出。

相關文章