redux connect 原理解析 传入的mapstate 和mapdispatch connect()第一次执行后返回一个函数 这个函数接收到的参数是一个组件 对组件进行加工返回
高姐阶件调用
mapstatetoProps的调用在connect 函数内订阅执行 createStore的过程 对应 react hooks中 useReducer的过程
const mapStateToProps = (state) => {
return {
themeColor:state.themeColor
};
}
Content = connect(mapStateToProps)(Content);
下面是详细内容
一、目的
之前写过一篇文章,主要是看 react.js 小书的时候,也思考了一下 redux 这种模式(或者说 flux 这种模式)所解决的问题的痛点。
至少应该知道,他们为什么会产生,以及产生的过程中逐步解决了什么问题。
感兴趣的可以全看看 react.js 小书。
之前的文章主要是在完全不使用 react 的情况下写一个 redux 模式的应用,基本的内容如下链接:
有了这个前提,又考虑了如何在 React 组件中使用 redux 模式。
如果直接使用 redux
和 react-redux
这两个库的话,应该都知道下面几个概念:
Provider
connect
createStore
mapStateToProps
mapDispatchToProps
- ...
二、前提
1、createStore
实现一个简单的 react-redux ,首先需要有 createStore 的基本逻辑,能够进行观察。
下面的 createStore 就是之前文章中写出来的:
/**
* createStore
* - 用来创建 store
* - 实现了观察者模式
* - 对 reducer 进行了处理
* - 默认会首先 dispatch 一次空对象,也就是通过 reducer 生成初始化的数据
* @param {*} reducer
*/
function createStore(reducer) {
let state = null; // 初始化 state 是 null
const listeners = []; // 监听者是空
const subscribe = (listener) => listeners.push(listener); // 如果有监听者,则将其 push 进数组
const getState = () => state; // 返回 state
const dispatch = (action) => {
// 触发 action
state = reducer(state, action); // 使用 reducer 覆盖原来的 state
listeners.forEach((listener) => listener());// 对所有监听者进行更新操作
};
dispatch({});// 默认进行一次初始化state
return {getState, dispatch, subscribe}; // 返回 store 对象
}
2、reducer
面对业务逻辑还需要一个 reducer,比如我直接用的 react.js 小书中的 themeReducer
:
/**
* themeReducer
* @param {*} state
* @param {*} action
*/
function themeReducer(state, action) {
// 默认 color red
if (!state) {
return {themeColor: 'red'};
}
// 现在只写了一个 action
switch (action.type) {
case 'CHANGE_COLOR':
return {
...state,
themeColor: action.data
};
default:
return state;
}
}
三、实现业务逻辑
1、connect 高阶组件
实际上 connect 就是一个高阶组件,它将组件包装之后拿到一个能够在组件中直接获取 context 的 state 的组件:
connect 的实现方案在注释中写的很明显:
connect 的作用很明显,把一个组件进行包装之后,能够直接拿到 store 中的 state,此时,state 是作为
props 下发的,当然,组件本身接收的 props 不会有任何的影响。
只实现了 mapToStateProps 和 mapToDispatchProps ,需要引入
prop-types
/**
* 高阶组件 connect
* @param {*} mapStateToProps
* @param {*} mapDispatchToProps
*/
export const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => {
class Connect extends Component {
constructor() {
super();
this.state = {
allProps: {}
};
}
// context 约束必须
static contextTypes = {
store: PropTypes.object
};
// 组件挂在前需要执行的操作
componentWillMount() {
const {store} = this.context; // 从上下文中获取 store 该 store 是从根组件传递过来的
this._updateProps();// 初始化执行一次 updateProps
store.subscribe(() => this._updateProps()); // 加入观察者
}
// 用于更新 props
_updateProps() {
const {store} = this.context;
let stateProps = mapStateToProps
? mapStateToProps(store.getState(), this.props)
: {}; // 主要用来进行 store 的 state 的获取
let dispatchProps = mapDispatchToProps
? mapDispatchToProps(store.dispatch, this.props)
: {} // 用来 dispatch 的时候获取 store 的 dispatch
// 整合普通的 props 和 从 state 生成的 props
// 作为完整的 state 返回,这样在子组件中就能够通过 props 获取内容
this.setState({
allProps: {
...stateProps,
...dispatchProps,
...this.props
}
})
}
render() {
return <WrappedComponent {...this.state.allProps}/>
}
}
// 返回高阶组件
return Connect;
}
四、使用 connect
有了 connect 就能够在组件中去使用:
下面代码和 react-redux 基本上是一样的
const mapStateToProps = (state) => {
return {
themeColor:state.themeColor
};
}
Content = connect(mapStateToProps)(Content);