React Redux 是 Redux 的官方 React UI 绑定层。它允许您的 React 组件从 Redux 存储读取数据,并将操作分派到存储以更新状态。
# 1、问题剖析
TIP
经过前面 redux 的学习,我们可以先使用 react 写一个简单的counter 案例(redux 版) (opens new window) 😂
结果发现:
store 定义时是在正常的、相对便捷的(store 进行了拆分),但在使用 redux 时有大量重复的代码出现
重复代码
// 重复1
import store from './store/index.js'
class Demo extends React.PureComponent {
constructor() {
super()
this.state = {
// 重复2
count: store.getState().counter
}
}
componentDidMount() {
// 重复3
this.unsubscribe = store.subscribe(() => {
const state = store.getState()
this.setState({
count: state.counter
})
})
}
// 重复4
upDownHandle(str) {
store.dispatch(countChangeAction(str))
}
// ……
componentWillUnMount() {
// 重复5
this.unsubscribe()
}
}
从本质上讲,产生这种现象的原因时 react 和 redux 之间的关联不够。
# 2、react-redux ✨
上述问题我们可以使用高阶组件解决,但这些高阶组件不用我们自己封装。
npm install redux react-redux
redux 官方为了我们在方便在 react 中使用 redux,已经为我们提供了 react-redux (opens new window) 库用于将 react 和 redux 进行关联
我们可以使用其提供的高级组件 :Provider、connect()等达到解耦合、优化使用体验的目的
TIP
现在,我们可以使用 react-redux 重写一下counter 案例(react-redux 版) (opens new window),以解决在 react 中使用 redux 时发现的部分问题。
# 3、Provider 组件
Provider 可以为除根组件以外的所有组件提供 store
// index.js
import { Provider } from 'react-redux'
import store from './store'
// ……
root.render(
// 解决:重复 1
<Provider store={store}>
<App />
</Provider>
)
# 4、connect() 组件
connect 组件接收两个参数(函数),并返回一个新的高阶组件(函数)
connect 函数 ✍ 实现原理
- connect.js
import { PureComponent } from 'react'
// import store from '@/store'
export function connect(mapStateToProps, mapDispatchToProps) {
return function (WrapperComponent) {
class NewComponent extends PureComponent {
constructor(props, context) {
super(props)
// 🚩(state)
// this.state = mapStateToProps(store.getState())
this.state = mapStateToProps(this.context.getState())
}
componentDidMount() {
this.unsubscribe = store.subscribe(() => {
// this.forceUpdate()
// this.setState(mapStateToProps(store.getState()))
this.setState(mapStateToProps(this.context.getState()))
})
}
componentWillUnmount() {
this.unsubscribe()
}
render() {
// const stateObj = mapStateToProps(store.getState())
// const dispatchObj = mapDispatchToProps(store.dispatch)
const stateObj = mapStateToProps(this.context.getState())
const dispatchObj = mapDispatchToProps(this.context.dispatch) // 🚩(dispatch)
return <WrapperComponent {...this.props} {...stateObj} {...dispatchObj} />
}
}
// 新增
NewComponent.contextType = StoreContext
return NewComponent
}
}
- StoreContext.js
import { createContext } from 'react'
export const StoreContext = createContext() // 使用Context传递store
- index.js
export { StoreContext } from './StoreContext.js'
export { connect } from './connect.js'
- 使用
// index.js
import { Provider } from 'react-redux'
import store from './store'
import { StoreContext } from 'xxxx'
// ……
root.render(
<Provider store={store}>
<StoreContext.Provider value={store}>
<App />
</StoreContext.Provider>
</Provider>
)
connect() 组件实现:
react 中的 state、dispatch 进行映射(store 代码和 react 代码解耦)
import { connect } from 'react-redux'
import { countChangeAction } from '../store/actionCreators.js'
class Demo extends React.PureComponent {
//
upDownHandle(str) {
// 解决:重复3
// store.dispatch(countChangeAction(str))
this.props.countChange(str)
}
//
render() {
// 解决:重复2
// store.getState().counter
const { count } = this.props
return (
<div>
<p>app couter: {count}</p>
<button onClick={(e) => this.upDownHandle('+1')}>+1</button>
<button onClick={(e) => this.upDownHandle('+3')}>+3</button>
</div>
)
}
}
// 1、state映射
const mapStateToProps = (state) => ({
count: state.counter
})
// 2、dispatch映射
const mapDispatchToProps = (dispatch) => ({
countChange: function (str) {
dispatch(countChangeAction(str))
}
})
// connect() 返回的是一个高阶组件
export default connect(mapStateToProps, mapDispatchToProps)(Demo)