React-Redux API实现
react-redux提供了两个api
- Provider
- connect
Provide主要是将store分发传递下去,而我们在组件中需要去消费store时,可以使用connect将状态和派发函数映射到组件中直接使用。
下面对这两个api 实现 hook 版本。
手写 Provider
const Context = React.createContext()
export const Provider = ({ store, children }) => {
return (
<Context.Provider value={store}>
{children}
</Context.Provider>
)
}
实质就是利用了React的Context向下传递store,渲染包裹的children。
手写 connect ( Hook实现 )
connect是一个HOC工厂函数,对传入的组件附加额外的props,再返回。通过两个映射函数,将store中的状态和派发动作函数绑定到组件的props。同时订阅视图的更新。
可以将功能拆分为:
- 按照状态映射函数将状态映射到组件props
- 根据actionCreators函数,将真实派发action操作映射到组件props
- 订阅更新
const Context = React.createContext()
export const connect = (
mapStateToProps = state => state,
mapDispatchToProps = {}) => Component => props => {
// 3. 通过useContext拿到顶层组件传递的store
const store = useContext(Context)
//2. 将映射后的属性定义为一个新的状态,状态更新是重新render
const [moreProps, setMoreProps] = useState(getMoreProps())
function getMoreProps() {
return {
...mapStateToProps(store.getState()),
...bindActionCreators(mapDispatchToProps, store.dispatch)
}
}
// 4. 将action转换为可执行的函数,其实就是在外面包一层dispatch
function bindActionCreators(creators, dispatch) {
let actionObj = {}
for (let key in creators) {
actionObj[key] = bindActionCreator(creators[key], dispatch)
}
return actionObj
}
function bindActionCreator(creator, dispatch) {
// creator是一个返回action的函数,类似我们定义的 ()=> ({type:'ADD'})
return (...args) => dispatch(creator(...args))
}
// 5. 最后我们还要坐下视图的更新
useEffect(() => {
store.subscribe(() => {
// 初始化阶段,对moreProps进行一个订阅更新,每次状态的变更都会触发去重新getMoreProps来拿到最新的状态
setMoreProps({ ...moreProps, ...getMoreProps() })
})
}, [moreProps])
// 1. 接受一个组件,返回一个新的组件,同时给该组件传递一些新的props
return <Component {...props} {...moreProps} />
}
准备测试环境;
- 入口 index 引入
// store还是用之前计数器的栗子
import store from './store/myReactReduxStore'
// 这里引入我们自己写的ZReactRedux
import { Provider } from './ZReactRedux'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
, document.getElementById('root'));
- page页面
import React, { Component } from 'react'
// 这里换成我们自己写的ZReactRedux
import { connect } from '../ZReactRedux'
import { add, minus, asyncAdd } from '../action/counterAction'
class MyReactReduxPage extends Component {
state = {}
render() {
const { count, add, minus, asyncAdd } = this.props
return (
<div>
<p>{count}</p>
<button onClick={add}>加</button>
<button onClick={asyncAdd}>异步加</button>
<button onClick={minus}>减</button>
</div>
);
}
}
export default connect(
state => ({ count: state }),
{
add,
minus,
asyncAdd
}
)(MyReactReduxPage);
mapDispatchToProps 的两种传参处理
看过官方源码,我们发现其mapDispatchToProps这个参数做了三种判断
- 未传入该参数,直接将dispatch当做属性传入
- 传入一个对象
- 传入一个函数
现在我们来实现一下这个判断处理,先看下传入一个函数是怎么用的
connect(
state => ({ count: state }),
// 传入对象,action函数
{
add,
minus,
asyncAdd
}
// 传入一个函数,其实也是返回派发函数的具体实现
dispatch => ({
add: (...args) => dispatch({ type: 'ADD' }),
asyncAdd: (...args) => {
setTimeout(() => {
dispatch({ type: 'ADD' })
}, 1000)
}
})
)(MyReactReduxPage);
现在在 getMoreProps 中添加一些参数判断逻辑
function getMoreProps() {
let funsObj = {}
if (!mapDispatchToProps) {
// 默认将dispatch传入
funsObj = { dispatch: store.dispatch }
} else if (typeof mapDispatchToProps === 'function') {
// 传入函数
funsObj = mapDispatchToProps(store.dispatch)
} else {
// 传入对象
funsObj = bindActionCreators(mapDispatchToProps, store.dispatch)
}
return {
...mapStateToProps(store.getState()),
...funsObj
}
}