> 文档中心 > 集中式状态管理<react-redux>基本使用与优化

集中式状态管理<react-redux>基本使用与优化

在这里插入图片描述

欢迎来到我的博客
📔博主是一名大学在读本科生,主要学习方向是前端。
🍭目前已经更新了【Vue】、【React–从基础到实战】、【TypeScript】等等系列专栏
🛠目前正在学习的是🔥 React框架 React框架React框架🔥,中间穿插了一些基础知识的回顾
🌈博客主页👉codeMak1r.的博客

😇本文目录😇

  • 1、react-redux
  • 2、连接容器组件与UI组件
  • 3、react-redux基本使用
  • 优化1、简写mapState和mapDispatch两个映射方法
  • 优化2、Provider组件的使用
  • 优化3、整合UI组件与容器组件
  • 优化总结

本文被专栏【React–从基础到实战】收录

🕹坚持创作✏️,一起学习📖,码出未来👨🏻‍💻!
在这里插入图片描述

1、react-redux

在这里插入图片描述
react-redux把组件分为两类,一类叫做UI组件,一类叫做容器组件;

UI组件的外侧都要包裹一个容器组件。

注意⚠️:本文使用的示例为之前的文章中的示例。在之前的文章中已经做过了介绍,在本文就不再阐述。

建议阅读顺序是:
【redux工作流程】
【redux异步action】
👉再到本文

2、连接容器组件与UI组件

UI组件存放在components文件夹中,我们可以在src目录下新建文件夹containers,专门用于存放容器组件

例如:Count组件的容器组件:/src/containers/Count/index.jsx

/*  该组件是Count组件这个UI组件的容器组件 */// 引入Count组件的UI组件import CountUI from '../../components/Count'// 引入connect用于连接UI组件与容器组件import { connect } from 'react-redux'//创建并暴露一个Count的容器组件,connect()()的含义是:调用connect()这个函数返回的函数export default connect()(CountUI)

代码解释:

引入CountUI,为Count组件的UI组件,从原理图中来看,该组件是Count容器组件的子组件。

引入connect函数,用于连接UI组件与容器组件;

我们从原理图中可以看出,redux要想工作,必须要连接UI组件与容器组件,还必须连接容器组件与store。

那么我们在容器组件中,使用connect()()的方法连接了UI组件

【connect()()的含义:调用connect()这个函数的返回值,返回值本身是一个函数,也就是调用返回的函数】

现在还需要让容器组件与store建立起连接;

怎么让容器组件与store建立起连接呢?

我们之前在App组件中一直都是渲染Count的UI组件的,但是我们现在引入了Count的容器组件(为Count的UI组件的父组件),那么我们可以直接在App组件中直接渲染Count组件的容器组件:

// App.jsximport React, { Component } from 'react'import Count from './containers/Count'import store from './redux/store'export default class App extends Component {  render() {    return (      <div>      {/* 给容器组件传递store */} <Count store={store} />      </div>    )  }}

重点⚠️在于,

在渲染Count组件的容器组件时,通过组件的props属性,将store传入Count组件的容器组件,从而实现容器组件与store建立起连接。

3、react-redux基本使用

现在我们已经把UI组件与容器组件建立起连接了,并且将容器组件与store也建立起连接了,那么我们接下来一起来探索react-redux是怎么工作,如何使用的呢?

从流程图可以看出,我们需要向UI组件传递state(状态)和dispatch(操作状态的方法)。

但是我们在UI组件的父组件,也就是Count的容器组件中,并没有以组件标签()的形式去渲染子组件

而是通过react-redux提供的connect方法将父子组件建立起的联系,我们就需要通过connect方法传递props。

我们之前知道,我们在调用connect方法返回的函数时需要传递UI组件作为参数,以将容器组件与UI组件连接起来,也就是

connect()(CountUI)

而传递props时,我们需要在调用connect方法时直接传入两个参数(也就是connect的第一个括号内的参数),并且这两个参数都是函数形式的参数。具体看代码吧~

connect(mapStateToProps, mapDispatchToProps)(CountUI)

说明:第一个函数参数mapStateToProps作用是将存放在store中的状态映射给UI组件的props;

第二个函数参数mapDispatchToProps作用是将操作数据的方法映射给UI组件的props;

其实也就对应图中的

在这里插入图片描述

具体实现如下:

/*  该组件是Count组件这个UI组件的容器组件 */import CountUI from '../../components/Count'import { connect } from 'react-redux'import {  createIncrementAction,  createDecrementAction,  createIncrementAsyncAction} from '../../redux/count_action_creator'/*   1.mapStateToProps函数返回的是一个对象;  2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value  3.mapStateToProps用于传递状态*/function mapStateToProps(state) {  return { count: state }}function mapDispatchToProps(dispatch) {  return {    jia: (data) => {      // 通知redux执行加法      dispatch(createIncrementAction(data))    },    jian: (data) => {      dispatch(createDecrementAction(data))    },    jiaAsync: (data, time) => {      dispatch(createIncrementAsyncAction(data, time))    }  }}//创建并暴露一个Count的容器组件,connect()()的含义是:调用connect()这个函数返回的函数export default connect(mapStateToProps, mapDispatchToProps)(CountUI)

这样一来,Count的UI组件就会接收到传递进来的props,我们可以通过props去渲染状态和操作状态:

/src/components/Count/index.jsx:

渲染状态:

<h1>当前求和为:{this.props.count}</h1>

操作状态:

// 加法increment = () => {  const { value } = this.selectedNumber  this.props.jia(value * 1)}// 减法decrement = () => {  const { value } = this.selectedNumber  this.props.jian(value * 1)}// 和为奇数时,加incrementIfOdd = () => {  const { value } = this.selectedNumber  if (this.props.count % 2 !== 0) {    this.props.jia(value * 1)  }}// 异步加incrementAsync = () => {  const { value } = this.selectedNumber  this.props.jiaAsync(value * 1, 500)}

react-redux基本使用总结:

  1. 明确两个概念:UI组件与容器组件

    UI组件:不能使用任何redux的api,只负责页面的呈现、交互等等;

    容器组件:负责和redux通信,将结果交给UI组件

  2. 如何创建一个容器组件——靠react-redux 的 connect函数

    connect(mapStateToProps, mapDispatchToProps)(CountUI)

    mapStateToProps——映射状态,返回值是一个对象;

    mapDispatchToProps——映射操作状态的方法,返回值是一个对象

  3. 备注:容器组件中的store是靠props传入的,而不是在容器组件中直接引入。

优化1、简写mapState和mapDispatch两个映射方法

原映射方法:

function mapStateToProps(state) {  return { count: state }}function mapDispatchToProps(dispatch) {  return {    jia: (data) => {      // 通知redux执行加法      dispatch(createIncrementAction(data))    },    jian: (data) => {      dispatch(createDecrementAction(data))    },    jiaAsync: (data, time) => {      dispatch(createIncrementAsyncAction(data, time))    }  }}

简写成箭头函数:

const mapStateToProps = state => ({ count: state })const mapDispatchToProps = dispatch => (  {    jia: (data) => {      // 通知redux执行加法      dispatch(createIncrementAction(data))    },    jian: (data) => {      dispatch(createDecrementAction(data))    },    jiaAsync: (data, time) => {      dispatch(createIncrementAsyncAction(data, time))    }  })

当然,这只是在编码的角度对函数进行简化,我们还可以在API的角度,实现mapDispatch更为简洁的写法。

其实mapDispatchToProps还可以是一个对象,在一般写法中,mapDispatchToProps是一个函数,但是可以简写成一个对象。

export default connect(  state => ({ count: state }),  // mapDispatchToProps的简写——是一个对象  {    jia: createIncrementAction,    jian: createDecrementAction,    jiaAsync: createIncrementAsyncAction  }  // mapDispatchToProps一般写法——是一个函数  /* dispatch => (    {      jia: (data) => { // 通知redux执行加法 dispatch(createIncrementAction(data))      },      jian: (data) => { dispatch(createDecrementAction(data))      },      jiaAsync: (data, time) => { dispatch(createIncrementAsyncAction(data, time))      }    }  ) */)(CountUI)

把mapDispatchToProps简写成一个对象的话,react-redux会自动分发,也就是底层自动调用dispatch方法,所以我们在书写代码时并不需要手动调用dispatch方法,在mapDispatchToProps简写成一个对象时可以省略dispatch,这就是属于API层面的优化。

优化2、Provider组件的使用

在前面的连接容器组件和store的讲解中,我们提出:要想连接容器组件和store,就需要在App.jsx中通过props将store传入容器组件中。也就是这样子的:

// App.jsximport React, { Component } from 'react'import Count from './containers/Count'import store from './redux/store'export default class App extends Component {  render() {    return (      <div>      {/* 给容器组件传递store */} <Count store={store} />      </div>    )  }}

现在我们基于react-redux,可以对App组件进行优化:

// App.jsximport React, { Component } from 'react'import { Provider } from 'react-redux'import Count from './containers/Count'import store from './redux/store'export default class App extends Component {  render() {    return (      <div> {/* 给容器组件传递store */} <Provider store={store}>   <Count /> </Provider>      </div>    )  }}

这样一来,哪怕我们需要连接多个容器组件到store,都只需要在渲染组件标签的外围包裹一个标签即可。

优化3、整合UI组件与容器组件

前面两个优化,我们分别从代码层面和API层面进行了优化。在这里,我们将在文件层面对UI组件和容器组件进行优化。

我们前面在逐步学习实践react-redux时,将容器组件放在containers文件夹中。但是我们发现,如果需要使用redux的UI组件越来越多的话,需要的容器组件也越来越多,到最后可能项目中有20个组件需要使用redux中的状态,那么我们真的就另外创建20个容器组件吗?

O f c o u r s e n o t ! Of course not! Ofcoursenot!

其实我们可以将UI组件与容器组件整合在同一个文件内!

/src/containers/Count/index.jsx:

import React, { Component } from 'react'// 引入connect用于连接UI组件与容器组件import { connect } from 'react-redux'import {  createIncrementAction,  createDecrementAction,  createIncrementAsyncAction} from '../../redux/count_action_creator'// 定义UI组件Countclass Count extends Component {  // 加法  increment = () => {    const { value } = this.selectedNumber    this.props.jia(value * 1)  }  // 减法  decrement = () => {    const { value } = this.selectedNumber    this.props.jian(value * 1)  }  // 和为奇数时,加  incrementIfOdd = () => {    const { value } = this.selectedNumber    if (this.props.count % 2 !== 0) {      this.props.jia(value * 1)    }  }  // 异步加  incrementAsync = () => {    const { value } = this.selectedNumber    this.props.jiaAsync(value * 1, 500)  }  render() {    return (      <div> <h1>当前求和为:{this.props.count}</h1> <select ref={currentNode => { this.selectedNumber = currentNode }}>   <option value="1">1</option>   <option value="2">2</option>   <option value="3">3</option> </select>&nbsp;&nbsp;&nbsp; <button onClick={this.increment}></button>&nbsp; <button onClick={this.decrement}></button>&nbsp; <button onClick={this.incrementIfOdd}>和为奇数时,加</button>&nbsp; <button onClick={this.incrementAsync}>异步加</button>      </div>    )  }}// 创建并暴露一个Count的容器组件,connect()()的含义是:调用connect()这个函数返回的函数export default connect(  state => ({ count: state }),  // mapDispatchToProps的简写——是一个对象  {    jia: createIncrementAction,    jian: createDecrementAction,    jiaAsync: createIncrementAsyncAction  }  // mapDispatchToProps一般写法——是一个函数)(Count)

如此一来,我们就可以删除/src/components/Count/index.jsx了~

优化总结

  1. 容器组件和UI组件整合为同一个文件

  2. 无需自己给容器组件传递store,给包裹一个标签,在Provider内传递store即可;

  3. 使用了react-redux后也不用再自己监测redux中状态的改变了,容器组件可以自动完成这个工作;

  4. mapDispatchToProps也可以简单的写成一个对象;

  5. 一个组件要和redux“打交道”要经过哪几步?

    1. 定义好UI组件——不暴露;
    2. 引入connect生成一个容器组件,并暴露,写法如下:
     connect(state => ({key:value}), //映射状态{key:xxxxAction}  //映射操作状态的方法 )(UI组件)
    1. 在UI组件中通过this.props读取和操作状态。