react-redux源码研读(版本:4.x)
最原始的使用redux数据流的方式是怎样的?
我们需要创建多个reducers,并且用combineReducers去合并我们的reducer,利用创建好的reducers,在用createStrore创建state状态,用Provider去包裹我们所有的组件并传递store数据。在使用出用connect链接到store,通过dispatch传递action传递type进去更改state的值。这样的流程大致是redux的工作流程。
第一步我们需要用combineReducers去合并reducer,combineReducers是怎样工作的呢?
combineReducers
接受一个对象,合并能力是由redux这个包提供的。
import { combineReducers } from 'redux'
import todos from './todos'
import visibilityFilter from './visibilityFilter'
const todoApp = combineReducers({
todos,
visibilityFilter
})
export default todoApp
使用时是这样使用的。看源码
export default function combineReducers(reducers: ReducersMapObject) {
const reducerKeys = Object.keys(reducers)
const finalReducers: ReducersMapObject = {}
for (let i = 0; i < reducerKeys.length; i++) {
const 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]
}
}
// 定义了变量去接受reducers
const finalReducerKeys = Object.keys(finalReducers)
// This is used to make sure we don't warn about the same
// keys multiple times.
let unexpectedKeyCache: { [key: string]: true }
if (process.env.NODE_ENV !== 'production') {
unexpectedKeyCache = {}
}
let shapeAssertionError: Error
// 检测传入的各个reducer是否正确
try {
assertReducerShape(finalReducers)
} catch (e) {
shapeAssertionError = e
}
// combineReducers得到的就是combination函数。在createStore有用到。
// combination函数执行得到的结果,是一个对象,键和值是reducer,其实是我们传入的那个样子
return function combination(
state: StateFromReducersMapObject<typeof reducers> = {},
action: AnyAction
) {
if (shapeAssertionError) {
throw shapeAssertionError
}
if (process.env.NODE_ENV !== 'production') {
const warningMessage = getUnexpectedStateShapeWarningMessage(
state,
finalReducers,
action,
unexpectedKeyCache
)
if (warningMessage) {
warning(warningMessage)
}
}
let hasChanged = false
const nextState: StateFromReducersMapObject<typeof reducers> = {}
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
const reducer = finalReducers[key]
const previousStateForKey = state[key]
const nextStateForKey = reducer(previousStateForKey, action)
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
hasChanged =
hasChanged || finalReducerKeys.length !== Object.keys(state).length
return hasChanged ? nextState : state
}
}
combineReducers做了什么?它对我们传入的reduce做检测,是否为合法的reducers。我认为这是核心。
createStrore
这个函数可以得到我们Provider所需要的store参数,它接收combineReducers返回的函数combination,第二个
参数是初始值。源码走起,只看最后返回的东西,同样这个函数是redux提供的能力之一
const store = ({
dispatch: dispatch as Dispatch<A>,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
} as unknown) as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
return store
很明显,返回的就是一个对象,里面包裹了dispatch,subscribe等函数。
Provider(react-redux)
服务消费者,运用的是react的context,传递了store下去。
export default class Provider extends Component {
getChildContext() {
return { store: this.store }
}
constructor(props, context) {
super(props, context)
this.store = props.store
}
render() {
return Children.only(this.props.children)
}
}
connect
是一个高阶函数,返回新的组件,添加我们定义的对象进props。消费provider提供的store,会将store的dispatch和mapStateToProps返回的对象添加进props供组件使用。用作触发更改store的值。
先看传递给provider的store是什么
import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux/src'
import { createStore } from 'redux/src'
import todoApp from './models/reducers/todos'
import App from './App'
const initState=[{aaa:'aaaa'}];
let store = createStore(todoApp,initState)
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
初始化了一个值为数组。
在看connect,这里贴关键代码,在connect高阶函数里面返回的是Connect组件,render函数内。
render() {
const {
haveOwnPropsChanged,
hasStoreStateChanged,
haveStatePropsBeenPrecalculated,
statePropsPrecalculationError,
renderedElement
} = this
this.haveOwnPropsChanged = false
this.hasStoreStateChanged = false
this.haveStatePropsBeenPrecalculated = false
this.statePropsPrecalculationError = null
if (statePropsPrecalculationError) {
throw statePropsPrecalculationError
}
let shouldUpdateStateProps = true
let shouldUpdateDispatchProps = true
// 第一次进来 renderElement没有直接跳过这个判断
if (pure && renderedElement) {
shouldUpdateStateProps = hasStoreStateChanged || (
haveOwnPropsChanged && this.doStatePropsDependOnOwnProps
)
shouldUpdateDispatchProps =
haveOwnPropsChanged && this.doDispatchPropsDependOnOwnProps
}
let haveStatePropsChanged = false
let haveDispatchPropsChanged = false
// 这个判断在每次render的时候都为false 跳过。
if (haveStatePropsBeenPrecalculated) {
haveStatePropsChanged = true
// 为true 进入
// 这里会顺序去调用合并,state,dispatch,最后将state,dispatch,添加到mergeProps里面去/
} else if (shouldUpdateStateProps) {
haveStatePropsChanged = this.updateStatePropsIfNeeded()
}
if (shouldUpdateDispatchProps) {
haveDispatchPropsChanged = this.updateDispatchPropsIfNeeded()
}
let haveMergedPropsChanged = true
if (
haveStatePropsChanged ||
haveDispatchPropsChanged ||
haveOwnPropsChanged
) {
haveMergedPropsChanged = this.updateMergedPropsIfNeeded()
} else {
haveMergedPropsChanged = false
}
if (!haveMergedPropsChanged && renderedElement) {
return renderedElement
}
if (withRef) {
this.renderedElement = createElement(WrappedComponent, {
...this.mergedProps,
ref: 'wrappedInstance'
})
} else {
// 这里可以看出我们合并的props是mergedProps
this.renderedElement = createElement(WrappedComponent,
this.mergedProps
)
}
return this.renderedElement
}
// 三个函数的调用方式和处理方式差不多,
updateStatePropsIfNeeded() {
const nextStateProps = this.computeStateProps(this.store, this.props)
if (this.stateProps && shallowEqual(nextStateProps, this.stateProps)) {
return false
}
this.stateProps = nextStateProps
return true
}
computeStateProps(store, props) {
if (!this.finalMapStateToProps) {
return this.configureFinalMapState(store, props)
}
const state = store.getState()
const stateProps = this.doStatePropsDependOnOwnProps ?
this.finalMapStateToProps(state, props) :
this.finalMapStateToProps(state)
if (process.env.NODE_ENV !== 'production') {
checkStateShape(stateProps, 'mapStateToProps')
}
return stateProps
}
configureFinalMapState(store, props) {
// const mapState = mapStateToProps || defaultMapStateToProps
// 前面有得到过这个。是由我们传入的参数
const mappedState = mapState(store.getState(), props)
const isFactory = typeof mappedState === 'function'
this.finalMapStateToProps = isFactory ? mappedState : mapState
this.doStatePropsDependOnOwnProps = this.finalMapStateToProps.length !== 1
if (isFactory) {
return this.computeStateProps(store, props)
}
if (process.env.NODE_ENV !== 'production') {
checkStateShape(mappedState, 'mapStateToProps')
}
return mappedState
}
总得来说connect就是把用户自定的state和dispatch映射到props上。这就是connect所具备的功能。
dispatch
我们调用props下的dispatch实际是触发的store中的dispatch,看代码
function dispatch(action: A) {
try {
isDispatching = true
// currentReducer 其实就是执行的combineReducers返回的combination
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
return action
}
当前的state最后得到在通过Provider得到的新的值去更新,
数据格式是
{
reducer:state,
reducer:state
}
是以reducer函数命名的对象,整个state是以reducer为key,state为值的键值对。
总结
redux的数据中心其实只是一个变量,他可以为任何的数据类型,我们每次dispatch的时候,实际是会去遍历所有的reducer之后封装成一个新的state,而action只是一个简单的对象而已,并没有很神奇的东西、action肯定会有一个type键去确认你当前所想做的事的描述。
主要运用的技术点:
- 高阶组件
- react的context