Redux 源码解读
1.redux-action createAction
redux-action的createAction要领就是给我们供应了天真的建立相符FSA规范的action的要领。
exports.default = createAction;
/**
返回建立action的函数
*/
function createAction(type, payloadCreator, metaCreator) {
var finalPayloadCreator = typeof payloadCreator === 'function' ? payloadCreator : _identity2.default;
/**
返回的函数
*/
var actionHandler = function actionHandler() {
var hasError = (arguments.length <= 0 ? undefined : arguments[0]) instanceof Error;
/**
返回的action
*/
var action = {
type: type
};
//依据传入的参数,实行payloadCreator猎取payload
var payload = hasError ? arguments.length <= 0 ? undefined : arguments[0] : finalPayloadCreator.apply(undefined, arguments);
if (!(payload === null || payload === undefined)) {
action.payload = payload;
}
if (hasError) {
// Handle FSA errors where the payload is an Error object. Set error.
action.error = true;
}
//依据传入的参数,实行metaCreator猎取payload
if (typeof metaCreator === 'function') {
action.meta = metaCreator.apply(undefined, arguments);
}
//可以看到 payloadCreator和metaCreator的参数都是用的传给actionHandler的参数
return action;
};
actionHandler.toString = function () {
return type.toString();
};
return actionHandler;
}
2.redux combineReducer
redux的combineReducer要领 用于将多个reducer,兼并成一个大的reducer函数,传给store。
用于剖析state树,每个reducer对应state的一个key对应的子state。比方poi的reducer对应的就是state[poi]。如许在将state传递给props时利于剖析。
reducer以对象的情势传入,finalReducers 寄存终究的reducer,finalReducerKeys寄存reducer的key
终究返回 combination函数 reducer范例的函数,接收state和action 返回state
state的情势是一个大对象下面每个reducer对应一个子state。
触发一个action,会遍历一切的reducer,
将该reducer的旧state和action传入,然后依据返回的新的state对象是不是转变,来决议
终究的返回的state是不是转变。
这里须要注重:因为state都是援用范例,这里比较是值比较
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
所以假如我们想要转变全局的state,须要在reducer中返回新的对象,而不是本来的state对象,
假如返回本来的对象,纵然对象里的值转变了,也不会引发全局state的转变。
*/
export default function combineReducers(reducers) {
var reducerKeys = Object.keys(reducers)
var finalReducers = {}
for (var i = 0; i < reducerKeys.length; i++) {
var key = reducerKeys[i]
if (process.env.NODE_ENV !== 'production') {
if (typeof reducers[key] === 'undefined') {
warning(`No reducer provided for key "${key}"`)
}
}
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
var finalReducerKeys = Object.keys(finalReducers)
return function combination(state = {}, action) {
/**
校验语法错误,reducer返回的state不能是undefined
*/
if (sanityError) {
throw sanityError
}
var hasChanged = false
var nextState = {}
for (var i = 0; i < finalReducerKeys.length; i++) {
var key = finalReducerKeys[i]
var reducer = finalReducers[key]
var previousStateForKey = state[key]
var nextStateForKey = reducer(previousStateForKey, action)
if (typeof nextStateForKey === 'undefined') {
var errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
nextState[key] = nextStateForKey
/**
所以假如我们想要转变全局的state,须要在reducer中返回新的对象,而不是本来的state对象,
假如返回本来的对象,纵然对象里的值转变了,也不会引发全局state的转变。
*/
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
return hasChanged ? nextState : state
}
}
3 redux applyMiddleware
运用中间件的目标是包装dispatch,在action传递给dispatch实行之前,须要经由中间件的层层处置惩罚,举行一些营业上的处置惩罚,决议action的走向。
源码完成了这类将函数数组,经由历程reducerRight的要领,完成层层嵌套的实行,到达中间件的完成。
/**
显现实行中间件,获得中间件的返回函数数组chain,然后应用compose要领,天生嵌套的实行chain
要领的包装dispatch函数,
中间件的情势是
(getState, dispatch)=> next => action => {
next(action);
}
*/
export default function applyMiddleware(...middlewares) {
return (createStore) => (reducer, preloadedState, enhancer) => {
var store = createStore(reducer, preloadedState, enhancer)
var dispatch = store.dispatch
var chain = []
var middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
/**
store.dispatch 就是第一个next 是last ware的next
(...args) => {
return ware0(ware1(ware2(last(...args))))
}
dispatch = ware0(ware1(ware2(last(...args))))
所以中间件中next传入后返回的函数就是我们须要的函数情势,
比方dispatch 须要的函数情势是 传一个action
*/
return {
...store,
dispatch
}
}
}
/**
reduceRight是数组的从右至左实行,
初始的参数是末了一个函数接收dispatch,
的到的一个action=>{
dispatch(action);
}
情势的函数,作为参数composed
f的情势是
next=>action=>{
}
终究构成的就是
(...args) => {
return funcs0(funcs1(funcs2(last(...args))))
}
*/
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
const last = funcs[funcs.length - 1]
const rest = funcs.slice(0, -1)
return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
}
中间件实行历程模仿
中间件道理
*/
function func1 (next) {
console.log('func1 return');
return function (action) {
console.log('func1start');
next(action);
console.log('func1end');
}
}
function func2 (next) {
console.log('func2 return');
return function (action) {
console.log('func2start');
next(action);
console.log('func2end');
}
}
function func3 (next) {
console.log('func3 return');
return function (action) {
console.log('func3start');
next(action);
console.log('func3end');
}
}
function dispatch(action) {
console.log(action);
}
function afterCompose (args) {
return func1(func2(func3(args)));
}
var newdispatch = afterCompose(dispatch);
newdispatch('action');
/**
实行递次
func3 return
func2 return
func1 return
func1start
func2start
func3start
action
func3end
func2end
func1end
*/
4 redux createStore
运用场景拜见中间件的运用代码与applyMiddleware源码,是redux供应建立store的要领。
import isPlainObject from 'lodash/isPlainObject'
import $$observable from 'symbol-observable'
export var ActionTypes = {
INIT: '@@redux/INIT'
}
export default function createStore(reducer, preloadedState, enhancer) {
var currentReducer = reducer
var currentState = preloadedState
var currentListeners = []
var nextListeners = currentListeners
var isDispatching = false
function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice()
}
}
function getState() {
return currentState
}
/**
定阅监听
*/
function subscribe(listener) {
if (typeof listener !== 'function') {
throw new Error('Expected listener to be a function.')
}
var isSubscribed = true
ensureCanMutateNextListeners()
nextListeners.push(listener)
return function unsubscribe() {
if (!isSubscribed) {
return
}
isSubscribed = false
ensureCanMutateNextListeners()
var index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
}
}
/**
实行reducer,猎取state,实行listener
*/
function dispatch(action) {
try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
var listeners = currentListeners = nextListeners
for (var i = 0; i < listeners.length; i++) {
listeners[i]()
}
return action
}
/**
替代reducer
*/
function replaceReducer(nextReducer) {
if (typeof nextReducer !== 'function') {
throw new Error('Expected the nextReducer to be a function.')
}
currentReducer = nextReducer
dispatch({ type: ActionTypes.INIT })
}
/**
store建立的时刻,猎取初始的sate树
*/
dispatch({ type: ActionTypes.INIT })
return {
dispatch,
subscribe,
getState,
replaceReducer
}
}
5. react-redux Provider
redux和react的连系,Provider作为根组件,将store的state放在context中供子组件运用。
import { Component, PropTypes, Children } from 'react'
import storeShape from '../utils/storeShape'
import warning from '../utils/warning'
export default class Provider extends Component {
//把 store 放在context内里,给子元素用
getChildContext() {
return { store: this.store }
}
constructor(props, context) {
super(props, context)
this.store = props.store
}
render() {
const { children } = this.props
//衬着唯一的子元素
return Children.only(children)
}
}
Provider.propTypes = {
store: storeShape.isRequired,
children: PropTypes.element.isRequired
}
Provider.childContextTypes = {
store: storeShape.isRequired
}
6. react-redux connect
connect要领,将React的组件举行包装,包装的目标以下:
可以将store中指定的state,传递给组件当props
可以监听store中state的变化
可以将action传递给view
const defaultMapStateToProps = state => ({}) // eslint-disable-line no-unused-vars
const defaultMapDispatchToProps = dispatch => ({ dispatch })
const defaultMergeProps = (stateProps, dispatchProps, parentProps) => ({
...parentProps,
...stateProps,
...dispatchProps
})
export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {}) {
//返回包装组件的函数
return function wrapWithConnect(WrappedComponent) {
class Connect extends Component {
shouldComponentUpdate() {
return !pure || this.haveOwnPropsChanged || this.hasStoreStateChanged
}
constructor(props, context) {
super(props, context)
this.version = version
this.store = props.store || context.store
const storeState = this.store.getState()
this.state = { storeState }
this.clearCache()
}
isSubscribed() {
return typeof this.unsubscribe === 'function'
}
trySubscribe() {
if (shouldSubscribe && !this.unsubscribe) {
//定阅store的state变化
this.unsubscribe = this.store.subscribe(this.handleChange.bind(this))
this.handleChange()
}
}
tryUnsubscribe() {
if (this.unsubscribe) {
this.unsubscribe()
this.unsubscribe = null
}
}
componentDidMount() {
//定阅store的state变化
this.trySubscribe()
}
componentWillReceiveProps(nextProps) {
if (!pure || !shallowEqual(nextProps, this.props)) {
this.haveOwnPropsChanged = true
}
}
componentWillUnmount() {
this.tryUnsubscribe()
this.clearCache()
}
//定阅变化
handleChange() {
if (!this.unsubscribe) {
return
}
const storeState = this.store.getState()
const prevStoreState = this.state.storeState
if (pure && prevStoreState === storeState) {
return
}
if (pure && !this.doStatePropsDependOnOwnProps) {
const haveStatePropsChanged = tryCatch(this.updateStatePropsIfNeeded, this)
if (!haveStatePropsChanged) {
return
}
if (haveStatePropsChanged === errorObject) {
this.statePropsPrecalculationError = errorObject.value
}
this.haveStatePropsBeenPrecalculated = true
}
this.hasStoreStateChanged = true
//假如有变化 setState,触发render
this.setState({ storeState })
}
render() {
const {
haveOwnPropsChanged,
hasStoreStateChanged,
haveStatePropsBeenPrecalculated,
statePropsPrecalculationError,
renderedElement
} = this
//终究衬着组件,将兼并属性传递给WrappedComponent
if (withRef) {
this.renderedElement = createElement(WrappedComponent, {
...this.mergedProps,
ref: 'wrappedInstance'
})
} else {
this.renderedElement = createElement(WrappedComponent,
this.mergedProps
)
}
return this.renderedElement
}
}
Connect.displayName = connectDisplayName
Connect.WrappedComponent = WrappedComponent
Connect.contextTypes = {
store: storeShape
}
Connect.propTypes = {
store: storeShape
}
//把WrappedComponent的非静态react属性 复制到Connect,终究返回Connect
return hoistStatics(Connect, WrappedComponent)
}
}
7. redux bindActionCreators
运用实例拜见 react-redux connect 的运用实例
function bindActionCreator(actionCreator, dispatch) {
return (...args) => dispatch(actionCreator(...args))
}
将actionCreators绑定上dispatch,key照样actionCreators的key,然则多做了一层dispatch
*/
export default function bindActionCreators(actionCreators, dispatch) {
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
if (typeof actionCreators !== 'object' || actionCreators === null) {
throw new Error(
`bindActionCreators expected an object or a function, instead received ${actionCreators === null ? 'null' : typeof actionCreators}. ` +
`Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`
)
}
var keys = Object.keys(actionCreators)
var boundActionCreators = {}
for (var i = 0; i < keys.length; i++) {
var key = keys[i]
var actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
return boundActionCreators
}