# 手摸手实现react-redux
# 为什么要使用react-redux
我们先来看下没有使用 react-redux
之前我们是如何使用redux
的
import React, {Component} from 'react'
import { bindActionCreators } from 'redux'
import * as types from '../store/action-types'
import store from '../store'
function increment(payload) {
return {
type: types.INCREMENT,
payload,
}
}
function decrement(payload) {
return {
type: types.DECREMENT,
payload,
}
}
let action = bindActionCreators({
increment,
decrement
}, store.dispatch)
export default class Counter1 extends Component{
constructor(props) {
super(props)
this.state = {
number: store.getState().number
}
}
componentDidMount() {
this.unSubscribe = store.subscribe(() => {
this.setState({
number: store.getState().number
})
})
}
componentWillUnmount() {
this.unSubscribe()
}
render() {
return (
<div>
<p>{ this.state.number }</p>
<button onClick={action.increment}>+</button>
<button onClick={action.decrement}>-</button>
</div>
)
}
}
以上代码是应用redux
实现一个简单的加减计数器,我们发现如果我们在项目中使用redux
的话,每个组件都要重复以下逻辑
引入仓库store
利用
actionCreator
创建action
利用
bindActionCreators
优化diapatch的代码在组件内部动态绑定state
在组件的生命周期里面进行订阅和发布订阅
在组件事件中进行
action
派发等
假设我们的项目有100个组件,那就意味着以上的逻辑我们要复制粘贴100次,而且业务多起来之后代码量多很难维护,这也是为什么我们需要使用react-redux
的原因了。react-redux
的作用,就是将我们的组件和仓库连接在一起,简化一些重复的操作
# react-redux的基本用法
我们试下将上述的例子用react-redux
实现一下
// index.js
import React from 'react'
import ReactDOM from 'react-dom'
import Counter1 from './components/Counter1'
import {Provider} from 'react-redux' // 引入Provider组件作为父容器
import store from './store'
ReactDOM.render(<Provider store={store}>
<Counter1 />
</Provider>, window.root)
// counter.js
import React, {Component} from 'react'
import action from '../store/actions/conuter1'
import { connect } from 'react-redux'
import * as types from '../store/action-types'
class Counter1 extends Component{
// .. 这里省略了绑定state,订阅取消订阅等操作
// store的值和dispatch通过this.props获取
render() {
return (
<div>
<p>{ this.props.number }</p>
<button onClick={this.props.increment}>+</button>
<button onClick={this.props.decrement}>-</button>
</div>
)
}
}
let mapStateToProps = (state) => state
let mapDispatchToProps = (dispatch) => (
{
increment() {
dispatch({type: types.INCREMENT1})
},
decrement() {
dispatch({type: types.DECREMENT1})
}
}
)
export default connect(
mapStateToProps,
mapDispatchToProps
)(Counter1)
是不是简洁了很多,下面我们主要挑几个难懂的点来介绍一下,然后实现一个简单的react-redux
# mapStateToProps 和 mapDispatchToProps
看了上面代码,我们可能大致知道了 mapStateToProps
和 mapDispatchToProps
的作用是告诉 connect 组件需要的 state 内容和将要派发的动作,也就是负责一种映射的关系。
我们从代码 connect(mapStateToProps, mapDispatchToProps)(Counter1)
中可以知道,connect
函数将store
和dispatch
映射给Counter1
组件,所以在组件中我们可以通过this.props.xxx
来获取到store
的值和dispatch
的值,这个时候我们大致可以猜想一下是这样实现的<Counter1 {...store} {...dispatch} />
,事实上确实是这样的。
但是细心的同学会发现,connect
内部是怎么拿到store
和dispatch
的,我们只需要看下mapStateToProps
和 mapDispatchToProps
的结果答复呼之欲出了
那我们接下来总结一下react-redux
的作用吧
- 通过
Provider
组件将store
往下传递 - 通过
connent
将store
和dispatch
映射给每个组件,在组件中可以通过this.props.xxx
来使用
# 实现Provider
import React from 'react'
import Context from './context'
export default class Provider extends React.Component {
render() {
return (
<Context.Provider value={{ store: this.props.store }}>
{this.props.children}
</Context.Provider>
)
}
}
我们发现其实Provider
组件很简单,其实就是调用上下文的api, 将store的值传递给下层组件,而且Provider
组件自身不进行额外的渲染,我们只需要无条件的渲染子组件就完事了
# 实现connect - v1
上文中我们提到,react-redux
的作用是将组件和仓库连接在一起,简化了一些如订阅、绑定state等操作,也提到了mapStateToProps
和 mapDispatchToProps
的作用等,其实啊这些简化了的操作是在connect
函数中进行了的,相当于在上层进行了封装,高阶函数的用法.
实现 connect
import React from 'react'
import Context from '../components/context'
export default function(mapStateToProps, mapDispatchToProps) {
return function(Component) {
return class extends React.Component{
static contextType = Context
constructor(props, context) {
super(props)
this.state = mapStateToProps(context.store.getState()) // 拿到Provide组件通过上下文传递的store
}
componentDidMount() { // 进行订阅操作
this.unsubscribe = this.context.store.subscribe(() => {
this.setState(mapStateToProps(this.context.store.getState()))
})
}
componentWillUnmount() { // 进行取消订阅操作
this.unsubscribe()
}
render() {
let actions = mapDispatchToProps(this.context.store.dispatch)// 获取dispatch
return (
// 将store和dispatch传递给目标组件,实现组件内通过this.props.xxx来调用
<Component {...this.props} {...this.state} {...actions} />
)
}
}
}
}
# 实现connect - v2
但是这个还不是我们在日常项目中的最终用法,我们在日常项目中是这样使用的
// action/counter1.js
import * as types from '../action-types'
function increment(payload) { // actionCreator 简化action的创建的
return {
type: types.INCREMENT1,
payload,
}
}
function decrement(payload) {
return {
type: types.DECREMENT1,
payload,
}
}
export default {increment, decrement} //导出所有actionCreator
// Counter1.js
import action from '../store/actions/conuter1'
let mapStateToProps = (state) => state
// let mapDispatchToProps = (dispatch) => (
// {
// increment() {
// dispatch({type: types.INCREMENT1})
// },
// decrement() {
// dispatch({type: types.DECREMENT1})
// }
// }
// )
export default connect(
mapStateToProps,
action
)(Counter1)
我们发现我们省略了mapDispatchToProps
的写法,因为还是比较冗余业务多起来,我们只需要传入这个组件的actionCreator
就好了,那我们看下connect
函数是如何处理这种写法的吧
import React from 'react'
import Context from '../components/context'
import {bindActionCreators} from 'redux' // 引入bindActionCreators
export default function(mapStateToProps, mapDispatchToProps) {
return function(Component) {
return class extends React.Component{
static contextType = Context
constructor(props, context) {
super(props)
this.state = mapStateToProps(context.store.getState())
if (typeof mapDispatchToProps === 'function') { //mapDispatchToProps 写法
this.actions = mapDispatchToProps(context.store.dispatch)
} else if (typeof mapDispatchToProps === 'object') { // actions写法 是一个对象
this.actions = bindActionCreators(mapDispatchToProps, context.store.dispatch)
}
}
componentDidMount() {
this.unsubscribe = this.context.store.subscribe(() => {
this.setState(mapStateToProps(this.context.store.getState()))
})
}
componentWillUnmount() {
this.unsubscribe()
}
render() {
return (
<Component {...this.props} {...this.state} {...this.actions} />
)
}
}
}
}
我们可以利用redux
的bindActionCreators
来将传入的actions
转化为我们所需要actions的就好了
# 总结
redux、redux中间件原理、react-redux花了我大概1个多月的时间才把他们消化完......可能是因为现在工作负责的项目的技术栈没有react吧,最近写的少了对redux的用法生疏了,不管怎么说,继续加油。
接下来让我们来见识一下react-hooks
的神奇之处吧