react-redux
简介
- 所有的UI组件都应该包裹一个容器组件(父子关系)
- 容器组件是真正和
redux交互的,里面可以随意的使用redux的API - UI组件中不能使用任何
redux的API - 容器组件会传给UI组件:
redux中所保存的状态- 用于操作状态的方法
- 容器传给UI传递:状态、操作状态的方法均通过
props传递
案例前置准备
在上一篇《Redux入门》中我们有一个redux的案例,这也就直接在上面进行改进吧。
「目录结构」
- 改进
Count组件,去除里面任何与redux相关的内容,将其改造成一个UI组件。
import React, { Component } from 'react'
export default class Count extends Component {
state = {}
// 加法
increment = () => {
const { value } = this.selectNumber
}
// 减法
decrement = () => {
const { value } = this.selectNumber
}
// 求和结果为奇数再增加
incrementIfOdd = () => {
const { value } = this.selectNumber
}
// 模拟异步增加
incrementAsync = () => {
const { value } = this.selectNumber
}
render() {
return (
<div>
<h1>当前求和为:???</h1>
<select ref={c => this.selectNumber = c}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>求和结果为奇数再增加</button>
<button onClick={this.incrementAsync}>异步增加</button>
</div>
)
}
}
2.创建containers文件夹,新建一个Count容器组件,然后根据模型图来引入所需要的组件。
- 引入
Count的UI组件 - 引入
connect用于连接UI组件与redux - 使用
connect()()创建并暴露一个Count的容器组件
// 引入`Count`的UI组件
import CountUI from '../../components/Count'
// 引入`connect`用于连接UI组件与`redux`
import { connect } from 'react-redux'
// 使用`connect()()`创建并暴露一个`Count`的容器组件
export default connect()(CountUI)
3.通过App组件,引入store,再通过props属性传递给容器组件,这里需要注意的是,我们不能直接在容器组件中引入store,而是需要通过props来进传递,否则会报错提示找不到store。
import React, { Component } from 'react'
import store from './redux/store'
import Count from './containers/Count'
export default class App extends Component {
render() {
return (
<div>
<Count store={store}></Count>
</div>
)
}
}
4.父子组件传参
在常规的父子组件中,我们传递参数的方式是在父标签中引入子标签,同时申明参数即可,如:
<Father>
<Son a='1'/>
</Father>
但是在containers中,我们并没有类似的明显父子关系结构,难道容器组件和UI组件并不是父子关系吗?我们可以通过浏览器的插件看一下。
通过插件我们可以看到容器与UI确实是父子关系,那么父子间的参数传递应该如何实现呢?
在connect的函数中,我们可以传递两个参数交给它
- redux中所保存的状态
- 用于操作状态的方法
需要注意的是,这两个参数需要定义成函数,我们要传递给子组件的内容需要通过函数的返回值进行传递,案例如下:
// 引入`Count`的UI组件
import CountUI from '../../components/Count'
// 引入`connect`用于连接UI组件与`redux`
import { connect } from 'react-redux'
function a(){
return {a: 'aa'}
}
function b(){
return {b: 11}
}
// 使用`connect()()`创建并暴露一个`Count`的容器组件
export default connect(a, b)(CountUI)
在UI组件中,我们在render()中打印一下props看看能不能正常接收到传递的参数。
可以看到在props中是有我们传递过来到状态的。既然参数已经传递进来了,那么我们就可以改进一下,把我们需要的数据给拿到。
// 引入`Count`的UI组件
import CountUI from '../../components/Count'
// 引入`connect`用于连接UI组件与`redux`
import { connect } from 'react-redux'
import {createIncrementAction, createDecrementAction, createIncrementAsyncAction} from '../../redux/count_action'
// mapStateToProps函数返回一个对象:
// 1. key作为传递给UI组件的props的key,value作为传递给UI组件的props的value
// 2. 用于传递状态
function mapStateToProps(state){
return {count: state}
}
// mapDispatchToProps函数返回一个对象:
// 1. key作为传递给UI组件的props的key,value作为传递给UI组件的props的value
// 2. 用于传递操作状态的方法
function mapDispatchToProps(dispatch){
return {
increment: value => {dispatch(createIncrementAction(value * 1))},
decrement: value => {dispatch(createDecrementAction(value * 1))},
asyncIncrement: (value, time) => {dispatch(createIncrementAsyncAction(value * 1, time))},
}
}
// 使用`connect()()`创建并暴露一个`Count`的容器组件
export default connect(mapStateToProps, mapDispatchToProps)(CountUI)
import React, { Component } from 'react'
export default class Count extends Component {
// 加法
increment = () => {
const { value } = this.selectNumber
this.props.increment(value)
}
// 减法
decrement = () => {
const { value } = this.selectNumber
this.props.decrement(value)
}
// 求和结果为奇数再增加
incrementIfOdd = () => {
const { value } = this.selectNumber
if (this.props.count % 2 !== 0) {
this.props.increment(value)
}
}
// 模拟异步增加
incrementAsync = () => {
const { value } = this.selectNumber
this.props.asyncIncrement(value, 1000)
}
render() {
return (
<div>
<h1>当前求和为:{this.props.count}</h1>
<select ref={c => this.selectNumber = c}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>求和结果为奇数再增加</button>
<button onClick={this.incrementAsync}>异步增加</button>
</div>
)
}
}
求和结果的数值可以正常的显示了,并且几个按钮的功能也都可以正常使用。
两个函数的名字使用的是官网给出的提示定义的,具体复现方式如下:
function mapStateToProps(state){
return 1
}
function mapDispatchToProps(dispatch){
return 1
}
export default connect(mapStateToProps, mapDispatchToProps)(CountUI)
当我们传入当参数返回类型不是对象对适合,它会给出我们对应的错误提示,这里可以看到两个函数的名称。
优化
在上面的代码中,我们是通过App组件来传递store的,如果有多个组件,就需要重复多次传参,这样非常的繁琐,react-redux框架帮我们封装了一个方便方便的功能Provider,它可以在根标签那边引入,自动帮我们其余的组件加上store。同时,react-redux框架也帮我们实现了监听功能,我们也不需要在index.js文件中实现监听逻辑,可以直接去掉对应的监听。
// 引入React核心库
import React from 'react'
// 引入ReactDOM
import { createRoot } from 'react-dom/client'
import { Provider } from 'react-redux'
// 引入自定义组件
import App from './App.js'
import store from './redux/store'
// 渲染组件到页面
const container = document.getElementById('root')
const root = createRoot(container)
root.render(
<Provider store={store}>
<App/>
</Provider>
)
对于容器组件,两个参数函数也可以进一步的优化
// 引入`Count`的UI组件
import CountUI from '../../components/Count'
// 引入`connect`用于连接UI组件与`redux`
import { connect } from 'react-redux'
import { createIncrementAction, createDecrementAction, createIncrementAsyncAction } from '../../redux/count_action'
// 使用`connect()()`创建并暴露一个`Count`的容器组件
export default connect(
// mapStateToProps,
state => ({ count: state }),
// mapDispatchToProps的一般写法
// dispatch => ({
// increment: value => { dispatch(createIncrementAction(value * 1)) },
// decrement: value => { dispatch(createDecrementAction(value * 1)) },
// asyncIncrement: (value, time) => { dispatch(createIncrementAsyncAction(value * 1, time)) },
// })
// mapDispatchToProps的简写
{
increment: createIncrementAction,
decrement: createDecrementAction,
asyncIncrement: createIncrementAsyncAction,
}
)(CountUI)
小结
-
明确两个概念:
1)UI组件:不能使用任何
redux的API,只负责页面的呈现、交互等2)容器组件:负责和
redux通信,将结果交UI组件 -
创建容器组件:
1)通过
react-redux的connect()函数创建- mapStateToProps: 映射状态,返回值是一个对象 - mapDispatchToProps: 映射操作状态的方法,返回值是一个对象,本身也可以是一个对象 -
容器组件中的
store是靠props传进去的,不能在容器组件中直接引用
本文正在参加「金石计划 . 瓜分6万现金大奖」