Open JoeBBChen opened 6 years ago
middleware 的函数签名是 ({ getState, dispatch }) => next => action 函数签名:(或者类型签名,抑或方法签名)定义了 函数或方法的输入与输出。
middleware 的函数签名是 ({ getState, dispatch }) => next => action
函数签名:(或者类型签名,抑或方法签名)定义了 函数或方法的输入与输出。
dispatch(actions.getOrderList(tab));
dispatch 的参数是什么?
dispatch
function dispatch(action) { // 这里要求我们的 action 是一个纯对象 if (!isPlainObject(action)) { throw new Error( 'Actions must be plain objects. ' + 'Use custom middleware for async actions.' ) } // 略 }
那么这个是怎么实现的呢?,其实是用到中间件 redux-thunk ,文章主要讲 Redux middleware 的实现,顺带提到解释文章开头的问题。
redux-thunk
假设现在有两个 middleware:
const log = ({getState, dispatch}) => (next) => (action) => { console.log('action: ', action); return next(action); } // 这里跟 `redux-thunk` 源码雷同 const thunk = ({getState, dispatch}) => (next) => (action) => { if( typeof action === 'function') { return action(dispatch, getState); } return next(action); }
中间件的调用如下:
const store = createStore(rootReducer,applyMiddleware(thunk, log));
rootReducer 就是我们全局的 reducer。接着看 applyMiddleware 的源码。
rootReducer
reducer
applyMiddleware
export default function applyMiddleware(...middlewares) { return (createStore) => (reducer, preloadedState, enhancer) => { const store = createStore(reducer, preloadedState, enhancer) let dispatch = store.dispatch let chain = [] const middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) } chain = middlewares.map(middleware => middleware(middlewareAPI)) return { ...store, dispatch } } }
还有 createStore 的源码
createStore
export default function createStore(reducer, preloadedState, enhancer) { if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') { enhancer = preloadedState preloadedState = undefined } if (typeof enhancer !== 'undefined') { if (typeof enhancer !== 'function') { throw new Error('Expected the enhancer to be a function.') } return enhancer(createStore)(reducer, preloadedState) } // 略
经过转换,enhancer 就是 applyMiddleware。接下来看 enhancer(createStore)(reducer, preloadedState)
enhancer
enhancer(createStore)(reducer, preloadedState)
得到以下:
const enhancer = (createStore) => (reducer, preloadedState, enhancer) => { const store = createStore(reducer, preloadedState, enhancer) let dispatch = store.dispatch let chain = [] const middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) } chain = middlewares.map(middleware => middleware(middlewareAPI)) return { ...store, dispatch } } const store = createStore(rootReducer,enhancer); => const store = enhancer(createStore)(reducer, preloadedState);
这里有两个步骤:
const store_1 = enhancer(createStore); const store_2 = store_1(reducer, preloadedState);
store_1得到:
store_1
const store = (reducer, preloadedState, enhancer) => { const store = createStore(reducer, preloadedState, enhancer) let dispatch = store.dispatch let chain = [] const middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) } chain = middlewares.map(middleware => middleware(middlewareAPI)) return { ...store, dispatch }
store_2即:
store_2
const store = createStore(reducer, preloadedState, enhancer) let dispatch = store.dispatch let chain = [] const middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) } //middlewares = [thunk, log] chain = middlewares.map(middleware => middleware(middlewareAPI)) // 执行上面那句,得到 chain = [(next) => (action) => { console.log('action: ', action); return next(action); }, (next) => (action) => { if( typeof action === 'function') { action(dispatch, getState); } return next(action); }] dispatch = compose(...chain)(store.dispatch) // #1 return { ...store, dispatch }
接着看 compose:
compose
export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, b) => (...args) => a(b(...args))) }
reduce api 自己查作用。
reduce
// 假设 func = [a, b, c]; funcs.reduce((a, b) => (...args) => a(b(...args))) => (...args) => a(b(c(...args))); //所以两个中间件得到 thunk(log(args)) //compose return return (...args) => thunk(log(args));
所以 #1 得到:
#1
dispatch = ((...args) => thunk(log(args)))(store.dispatch) dispatch = thunk(log(store.dispatch)) /* log and thunk [(next) => (action) => { console.log('action: ', action); return next(action); }, (next) => (action) => { if( typeof action === 'function') { action(dispatch, getState); } return next(action); }] */ dispatch = (action) => { if( typeof action === 'function') { action(dispatch, getState); } return ((action) => { console.log('action: ', action); return store.dispatch(action); })(action); }
所以中间件的链式调用主要是通过 next 实现的。当一个中间件执行完成后,调用下一个中间件,即 next,在例子thunk(log(store.dispatch)) 中,thunk 的 next 就是 log,log 的 next 就是 store.dispatch,在 compose 已经完成 next 参数的传值,最后调用 dispatch 传进的参数为 action。
next
thunk(log(store.dispatch))
React 和 Redux,一个是负责 UI,一个是负责数据管理,其实两者并没有关联,它们之间的关联是使用了 react-redux。
react-redux 最核心的两个是 Provider 和 connect。
Provider
connect
先讲一下 React 组件通信的方式:
props
栗子:
//父组件: class Menu extends React.Component{ getChildContext(){ return {name: 'bb'}; } } Menu.childContextTypes = { name: Protypes.string } //子子子组件: Child.contextTypes = { name: Protypes.string } this.context.name 可访问父组件的 context
第四种方式就是 connect 获取全局 store 的方式。
store
Provider 用于包含最顶层的入口组件,例如:
const Root = ( <Provider store={store}> <App/> </Provider> ); ReactDOM.render(Root, window.document.getElementById("app"));
store 是通过 createStore 创建的。 Providor 源码:
Providor
export function createProvider(storeKey = 'store', subKey) { const subscriptionKey = subKey || `${storeKey}Subscription` class Provider extends Component { constructor(props, context) { super(props, context) this[storeKey] = props.store; } // 定义子组件能读取的属性 getChildContext() { return { [storeKey]: this[storeKey], [subscriptionKey]: null } } render() { return Children.only(this.props.children) } } if (process.env.NODE_ENV !== 'production') { Provider.prototype.componentWillReceiveProps = function (nextProps) { if (this[storeKey] !== nextProps.store) { warnAboutReceivingStore() } } } Provider.propTypes = { store: storeShape.isRequired, children: PropTypes.element.isRequired, } // 定义子组件读取的属性的类型 Provider.childContextTypes = { [storeKey]: storeShape.isRequired, [subscriptionKey]: subscriptionShape, } return Provider } export default createProvider()
Provider 模块的功能很简单,从最外部封装了整个应用,并向 connect 模块传递 store。
那么真正连接 React 和 Redux 的是 connect。
Redux 的运作是这样的,首先创建一个全局的 store 用于维护 state,如果要改变 state,则通过 dispatch 一个 action,reducer 通过相应的 action 去更新 state。
class Connect extends Component { constructor(props, context) { super(props, context) this.version = version this.state = {} this.renderCount = 0 // 这里便是获取到 Provider 中的 store this.store = props[storeKey] || context[storeKey] this.propsMode = Boolean(props[storeKey]) this.setWrappedInstance = this.setWrappedInstance.bind(this) invariant(this.store, `Could not find "${storeKey}" in either the context or props of ` + `"${displayName}". Either wrap the root component in a <Provider>, ` + `or explicitly pass "${storeKey}" as a prop to "${displayName}".` ) this.initSelector() this.initSubscription() } // 略 } const contextTypes = { [storeKey]: storeShape, [subscriptionKey]: subscriptionShape, } const childContextTypes = { [subscriptionKey]: subscriptionShape, } Connect.contextTypes = contextTypes Connect.propTypes = contextTypes
这段代码便是通过 context 的方式,获取到 Provider 中的全局 store, 总的步骤为
调用方式
const ProductCounterBox = connect(state => ({ isLogin: state['is_login'] }))(ProductCounter);
这时 ProductCounter 组件的 props 便包含 isLogin;
ProductCounter
isLogin
Redux 中还包含 subscribe 函数,用于注册事件,并在 dispatch 执行时发布事件。
subscribe
function subscribe(listener) { if (typeof listener !== 'function') { throw new Error('Expected listener to be a function.') } let isSubscribed = true ensureCanMutateNextListeners() // 将事件 push 到容器中,在 dispatch 遍历 nextListeners.push(listener) return function unsubscribe() { if (!isSubscribed) { return } isSubscribed = false ensureCanMutateNextListeners() const index = nextListeners.indexOf(listener) nextListeners.splice(index, 1) } } function dispatch(action) { // 略 // 这里遍历事件并触发 const listeners = currentListeners = nextListeners for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener() } return action }
那在 React-Redux 中注册了什么事件?
componentDidMount() { //略 this.subscription.trySubscribe() } trySubscribe() { if (!this.unsubscribe) { this.unsubscribe = this.parentSub ? this.parentSub.addNestedSub(this.onStateChange) : this.store.subscribe(this.onStateChange) this.listeners = createListenerCollection() } }
this.store.subscribe(this.onStateChange) 将事件注册到全局。
this.store.subscribe(this.onStateChange)
onStateChange() { this.selector.run(this.props) if (!this.selector.shouldComponentUpdate) { this.notifyNestedSubs() } else { this.componentDidUpdate = this.notifyNestedSubsOnComponentDidUpdate this.setState(dummyState) } }
在 onStateChange 中 setState,便触发 render 从而重新渲染子组件
onStateChange
setState
render
render() { const selector = this.selector selector.shouldComponentUpdate = false if (selector.error) { throw selector.error } else { return createElement(WrappedComponent, this.addExtraProps(selector.props)) } }
selector.props 保存的是组件需要的 props。
selector.props
function makeSelectorStateful(sourceSelector, store) { // wrap the selector in an object that tracks its results between runs. const selector = { run: function runComponentSelector(props) { try { const nextProps = sourceSelector(store.getState(), props) if (nextProps !== selector.props || selector.error) { selector.shouldComponentUpdate = true selector.props = nextProps selector.error = null } } catch (error) { selector.shouldComponentUpdate = true selector.error = error } } } return selector }
在 onStateChange 被触发的时候,会调用 this.selector.run(this.props)。这时候会通过 store.getState() 拿到最新的 state,与旧的 state 做比较,决定是否重新渲染组件。
this.selector.run(this.props)
store.getState()
总结: 文章解释了以下:
End By BBChen :stuck_out_tongue:
竟然不加入我大Vue,投入到react的怀抱~
@loocao 啊哈,那 Vue 带我飞吧。我写 React 要比写 Vue 要早~
vue比angular,react容易太多了啊~ 像我这种用angular2连Hello World都跑不起来的淫都能学会vue~
1、middleware
问题
dispatch
的参数是什么?dispatch 源码:
那么这个是怎么实现的呢?,其实是用到中间件
redux-thunk
,文章主要讲 Redux middleware 的实现,顺带提到解释文章开头的问题。例子
假设现在有两个 middleware:
中间件的调用如下:
rootReducer
就是我们全局的reducer
。接着看applyMiddleware
的源码。还有
createStore
的源码经过转换,
enhancer
就是applyMiddleware
。接下来看enhancer(createStore)(reducer, preloadedState)
得到以下:
这里有两个步骤:
store_1
得到:store_2
即:接着看
compose
:reduce
api 自己查作用。所以
#1
得到:所以中间件的链式调用主要是通过
next
实现的。当一个中间件执行完成后,调用下一个中间件,即 next,在例子thunk(log(store.dispatch))
中,thunk 的 next 就是 log,log 的 next 就是 store.dispatch,在 compose 已经完成 next 参数的传值,最后调用 dispatch 传进的参数为 action。2、react-redux
react-redux 最核心的两个是
Provider
和connect
。先讲一下 React 组件通信的方式:
props
传递数据给子组件props
传递事件给子组件,子组件在相应的地方调用该事件,将通信的数据通过函数参数传回到父组件函数中栗子:
第四种方式就是
connect
获取全局store
的方式。Provider
Provider
用于包含最顶层的入口组件,例如:store
是通过createStore
创建的。Providor
源码:Provider
模块的功能很简单,从最外部封装了整个应用,并向connect
模块传递store
。那么真正连接 React 和 Redux 的是
connect
。connect
Redux 的运作是这样的,首先创建一个全局的 store 用于维护 state,如果要改变 state,则通过 dispatch 一个 action,reducer 通过相应的 action 去更新 state。
这段代码便是通过 context 的方式,获取到
Provider
中的全局 store, 总的步骤为调用方式
这时
ProductCounter
组件的props
便包含isLogin
;React 响应 store
Redux 中还包含
subscribe
函数,用于注册事件,并在dispatch
执行时发布事件。那在 React-Redux 中注册了什么事件?
this.store.subscribe(this.onStateChange)
将事件注册到全局。在
onStateChange
中setState
,便触发render
从而重新渲染子组件selector.props
保存的是组件需要的 props。在
onStateChange
被触发的时候,会调用this.selector.run(this.props)
。这时候会通过store.getState()
拿到最新的 state,与旧的 state 做比较,决定是否重新渲染组件。总结: 文章解释了以下:
End By BBChen :stuck_out_tongue:
参考