概述

在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前,我须要发送一条日志,如果不应用中间件,你的做法是