redux和react-redux實現原理解析

概述

在react項目中,redux常常用來管理應用的數據,react-redux用來綁定redux, 這樣你的組件能夠從store中讀取數據,而且能夠dispatch actions更新store, redux主要思想讓系統中的數據按照統一的規則流動,即單項數據流
1.jpgjavascript

如圖: 對於組件的修改,經過action被dispatch到store, store根據action和當前的state計算出下一次的state, component拿到更新後的state從新渲染組件;能夠看到數據只是單項流動java

疑問

  • redux中間件redux-thunk,redux-lodder在上圖的單向數據流中其實作了什麼?
  • react組件如何訂閱store中state的變化,經過store.subscribe ? 那麼若是保證子組件的訂閱更新發生在父組件以後呢?(試想一下:父子組件都訂閱了store中某一個state的變化,若是父組件響應訂閱更新在子組件以後,子組件可能重複渲染)
  • react-redux中的hooks, useSelector, useDispatch, useStore等, 用useSelector替換connect方法?

問題分析

動手實現一個簡單的redux, react-redux, 中間件,而後對比一下咱們的實現有哪些潛在的問題,實際上怎麼解決的react

實現一個簡單的redux, react-redux

如何使用redux和react-redux, redux.createStore方法建立一個store, store注入到Provider組件上,而後經過connect方法拿到store中的state和dispatcher,而後經過組件的props拿到這些方法和屬性redux

createStore建立store,store對象具備dispatch方法,subscribe方法,getState方法, replaceReducer方法,那麼經過觀察者模式能夠實現數組

function createStore(reducer, preloadedState) {
          let listeners = []
          let state = preloadedState
          let currentReducer = reducer
          let subscribe = (fn) => {
                listeners.push(fn)
                return () => {
                   const index = nextListeners.indexOf(listener)
                   listeners.splice(index, 1)
                }
          }
          let dispatch = (action) => {
               state = currentReducer(state, action)
               for (let i = 0; i< listeners.length; i++) {
                   const listener = listeners[i]
                   listener()
               }
          }
          
          let getState = () => {
              return state
          }
          
          let repalceReducer = (nextReducer) => {
               currentReducer = nextReducer
               dispatch({type:'@@redux/REPLACE'}) 
          }
          
          
          return {
                dispatch
                subscribe,
                getState,
                replaceReducer    
          }
 }

redux實際實現要比這個複雜,好比說listeners存在兩個變量中, currentListeners和nextListeners,dispatch執行的老是currentListeners中的函數,subscribe和unsubscibe老是在nextListeners中增長或者移除listener,這樣能夠避免在dispatching過程當中,listeners數組發生改變,上面還能夠看到replaceReducer其實更新了state,觸發了訂閱, replaceReducer用在須要按需加載的reducer場景中,看下combiceReducers的實現,你會發現一次combine幾百個reducer並非一件好事,replaceReducer動態替換reducer提高效率app

接下來的問題如何在組件中訂閱state的更新,而且能夠dispatch action,以更新state;redux-redux使用了Context, redux中的Provider組件就是對Context.Provider作了封裝ide

export const ReactReduxContext = React.createContext(null)

function Provider({store, children, context}) {
     const [provider, setProvider] = useState({
         state: store.getState(),
         dispatch: store.dispatch
     })
     useEffect(() => {
        store.subcribe(() => {
             setProvider({
                 state: store.getState(),
                dispatch: store.dispatch
             })
        })
     }, [store])
     const Context = context || ReactReduxContext
     return <Context>{props.children}</Context>
}

export default Provider

有了Provider組件,咱們還須要一個connect函數,connect函數接收mapStateToProps和mapDispatchToProps,返回一個高階組件,這個高階組件接收React組件,返回一個PureComponent;函數

import ReactReduxContext from './Provider'
export default function connect (mapStateToProps, mapDispatchToProps) {
     return function (WrappedComment) {
           return function (props) {
                 return (
                     <ReactReduxContext>
                         {
                           ({state, dispatch}) => {
                           <WrappedComment {...mapStateToProps(state, props)} {...mapDispatchToProps(dispatch, props)} />        
                           }
                         }
                     </ReactReduxContext>
                 
                 )
                
           }           
     }
}

其實這個返回的組件,react-redux又用React.memo進行包裝,保證只在props發生變化的時候纔會從新渲染。react-redux對於connect的實現比這裏要複雜得多,如開頭提出的問題: react-redux須要保證父組件的更新在子組件以前,react的connect方法實際上是對connectAdvanced方法的封裝(參見官網),connectAdvanced方法放回一個高階組件,如上面所封裝的connect方法,返回的組件又使用React.memo變爲PureComponent, react-redux如何保證父組件訂閱store的更新,發生在子組件以前呢? 也是經過subscirpe方法;每個connect方法返回的高階組件內部都用了一個Context.Provider包裝WrappedComponent, 她的value爲store和Subscription對象,這樣每個子組件的Subscription對象能夠拿到離它最近的父組件的Subscription對象,這樣造成了一個Subscription對象樹,經過Subvcription控制更新順序, 直接上圖
react-redux.jpgspa

中間件實現

什麼是中間件?中間件就是對dispatch方法作了封裝,好比說每次dispatch一個action前,我須要發送一條日誌,若是不使用中間件,你的作法是日誌

相關文章
相關標籤/搜索