connect源码分析
- connect使用方式
import React, { Component } from 'react'
import { connect } from './react-redux'
class App extends Component {
render () {
return (
<div>
<p >{ this.props.age }}</p>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
age: state.age
}
}
export default connect(mapStateToProps)(App)
从使用方式可以看出,connect的作用就是将redux里面的store相应的参数赋值到connect包裹的组件的props上
- connect,实现方式
export const connect = (mapStateToProps) => (WrappedComponent) => {
class Connect extends Component {
// 获取到Provider提供的context必须要有
static contextTypes = {
store: PropTypes.object
}
constructor () {
super()
this.state = { allProps: {} }
}
componentWillMount () {
const { store } = this.context // 由于Provider设置了Context,这里可以访问到全局的context
this._updateProps() // 初始化
store.subscribe(() => this._updateProps()) // 当store的属性发生改变时,更新connect包裹的组件的属性值
}
_updateProps () {
const { store } = this.context
// 将mapStateToProps的所有属性传给connect包裹的组件
let stateProps = mapStateToProps(store.getState(), this.props)
this.setState({
allProps: { // 整合普通的 props 和从 state 生成的 props
...stateProps,
...this.props
}
})
}
render () {
return <WrappedComponent {...this.state.allProps} />
}
}
return Connect
}
connect 本质是一个函数柯里化的高阶组件,将mapStateToProps返回的对象作为其被包裹的组件的属性
- store.subscribe帮助connect监听数据变化,实现组件重新渲染
function createStore (reducer, state) {
const listeners = []
const getState = () => state
// subscribe接受一个监听函数,并将其放入监听队列中
const subscribe = (listener) => listeners.push(listener)
// 最关键的部分,每次dispatch的时候都会遍历监听队列,然后调用监听函数
const dispatch = (action) => {
reducer(state, action)
listeners.forEach((listener) => listener())
}
return { getState, dispatch, subscribe }
}
所以上面connect包裹的组件能够在store的数据发生改变的时候重新更新被包裹的组件
// 将_updateProps放入监听队列中
store.subscribe(() => this._updateProps())
// 每次dispatch的时候都会遍历监听队列,并且调用监听者_updateProps()实现connect被包裹的组件的更新
const dispatch = (action) => {
stateChanger(state, action)
listeners.forEach((listener) => listener())
}