redux和reactredux实现原理解析

4次阅读

共计 3202 个字符,预计需要花费 9 分钟才能阅读完成。

概述

在 react 我的项目中,redux 常常用来治理利用的数据,react-redux 用来绑定 redux, 这样你的组件能够从 store 中读取数据,并且能够 dispatch actions 更新 store, redux 次要思维让零碎中的数据依照对立的规定流动,即 单项数据流

如图:对于组件的批改,通过 action 被 dispatch 到 store, store 依据 action 和以后的 state 计算出下一次的 state, component 拿到更新后的 state 从新渲染组件;能够看到数据只是单项流动

疑难

  • redux 中间件 redux-thunk,redux-lodder 在上图的单向数据流中其实做了什么?
  • react 组件如何订阅 store 中 state 的变动,通过 store.subscribe?那么如果保障子组件的订阅更新产生在父组件之后呢?(试想一下:父子组件都订阅了 store 中某一个 state 的变动,如果父组件响应订阅更新在子组件之后,子组件可能反复渲染)
  • react-redux 中的 hooks, useSelector, useDispatch, useStore 等, 用 useSelector 替换 connect 办法?

问题剖析

入手实现一个简略的 redux, react-redux, 中间件,而后比照一下咱们的实现有哪些潜在的问题,实际上怎么解决的

实现一个简略的 redux, react-redux

如何应用 redux 和 react-redux, redux.createStore 办法创立一个 store, store 注入到 Provider 组件上,而后通过 connect 办法拿到 store 中的 state 和 dispatcher,而后通过组件的 props 拿到这些办法和属性

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 晋升效率

接下来的问题如何在组件中订阅 state 的更新,并且能够 dispatch action,以更新 state;redux-redux 应用了 Context, redux 中的 Provider 组件就是对 Context.Provider 做了封装

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 管制更新程序,间接上图

中间件实现

什么是中间件?中间件就是对 dispatch 办法做了封装,比如说每次 dispatch 一个 action 前,我须要发送一条日志,如果不应用中间件,你的做法是

正文完
 0