08_React redux

React redux

  • 一、理解
    • 1、学习文档
    • [2、redux 是什么吗](#2、redux 是什么吗)
    • [3、什么情况下需要使用 redux](#3、什么情况下需要使用 redux)
    • [4、redux 工作流程](#4、redux 工作流程)
    • [5、react-redux 模型图](#5、react-redux 模型图)
  • [二、redux 的三个核心概念](#二、redux 的三个核心概念)
  • [三、redux 的核心 API](#三、redux 的核心 API)
  • [四、使用 redux 编写应用](#四、使用 redux 编写应用)
    • [1、求和案例\_redux 精简版](#1、求和案例_redux 精简版)
      • [1.1 去除 Count 组件自身的状态](#1.1 去除 Count 组件自身的状态)
      • [1.2 src 下建立](#1.2 src 下建立)
      • [1.3 store.js](#1.3 store.js)
      • [1.4 count_reducer.js](#1.4 count_reducer.js)
      • [1.5 在 index.js 中检测 store 中状态的改变,一旦发生改变重新渲染](#1.5 在 index.js 中检测 store 中状态的改变,一旦发生改变重新渲染)
    • [2、求和案例\_redux 完整版](#2、求和案例_redux 完整版)
    • [3、求和案例\_redux 异步 action 版](#3、求和案例_redux 异步 action 版)
    • [4、求和案例\_react-redux 基本使用](#4、求和案例_react-redux 基本使用)
      • [4.1 明确两个概念](#4.1 明确两个概念)
      • [4.2 如何创建一个容器组件------ react-redux 的 connect 函数](#4.2 如何创建一个容器组件—— react-redux 的 connect 函数)
      • [4.3 备注 1:容器组件中的 store 是靠 props 传递的,而不是容器组件中直接引入 store](#4.3 备注 1:容器组件中的 store 是靠 props 传递的,而不是容器组件中直接引入 store)
      • [4.4 备注 2:mapDispatchToProps 也可以是一个对象](#4.4 备注 2:mapDispatchToProps 也可以是一个对象)
    • [5、求和案例\_react-redux 优化](#5、求和案例_react-redux 优化)
    • [6、多组件共享数据\_redux 管理](#6、多组件共享数据_redux 管理)
    • 7、纯函数
    • [8、Redux 开发者工具 redux-devtool](#8、Redux 开发者工具 redux-devtool)
  • 五、项目打包运行

一、理解

1、学习文档

1、英文文档:https://redux.js.org/

2、中文文档:http://www.redux.org.cn/

2、redux 是什么吗

1、redux 是一个专门用于做状态管理 的 JS 库(不是 react 插件库)

2、它可以用在 react、angular、vue 等项目中,但基本与 react 配合使用

3、作用:集中式管理 react 应用中多个组件共享的状态

3、什么情况下需要使用 redux

1、某个组件的状态,需要让其他组件可以随时拿到(共享)

2、一个组件需要改变另一个组件的状态(通信)

3、总体原则:能不用就不用,如果不用比较吃力才考虑使用

4、redux 工作流程

将公共的数据给到 redux,

5、react-redux 模型图

1、所有的 UI 组件都应该包裹一个容器组件,他们是父子关系。

2、容器组件时真正和 redux 打交道的,里面可以随意的使用 redux 的 api

3、UI 组件中不能使用任何 redux 的 api

4、容器组件会传给 UI 组件:

(1)redux 中所保存的状态

(2)用于操作状态的方法

5、备注:容器给 UI 传递:状态(这里的状态是 redux 中的状态)、操作状态的方法,均通过 props 传递

二、redux 的三个核心概念

1、action

1、动作的对象

2、包含 2 个属性:

type: 标识属性,值为字符串,唯一,必要属性

data: 数据属性,值类型任意,可选属性

3、例子:

javascript 复制代码
{type:'ADD_STUDENT',data:{name: 'Tom', age:18}}

action

如果是 Object 的一般对象就是 同步 action

如果值是 function 的对象就是 异步 action

2、reducer

1、用于初始化状态、加工状态

2、加工时,根据旧的 state 和 action,产生新的 state 的纯函数

3、store

1、将 state、action、reducer 联系在一起的对象

2、如何得到此对象?

1)import {createStore} from "redux"

2)import reducer from "./reducer"

3)const store = createStore(reducer)

3、此对象的功能?

1) getState():得到 state

2)dispatch(action):分发 action,触发 reducer 调用,产生新的 state

3)subscribe(listener):注册监听,当产生了新的 state 时,自动调用

三、redux 的核心 API

1、getState()

获取状态

2、dispatch()

分发

四、使用 redux 编写应用

求和应用

展示求和,进行 加、减、结果为奇数加、异步加 运算

1、求和案例_redux 精简版

1.1 去除 Count 组件自身的状态

1.2 src 下建立

-redux

-store.js

-count_reducer.js

1.3 store.js

1.3.1 引入 redux 中的 createStore 函数,创建一个 store

1.3.2 createStore 调用时要传入一个为其服务的 reducer

1.3.3 记得暴露 store 对象

1.4 count_reducer.js

1.4.1 reducer 的本质是一个函数,接收:preState,action ,返回加工后的状态

1.4.2 reducer 有两个作用:初始化状态,加工状态

1.4.3 reducer 被第一次调用时,是 store 自动触发的:

传递的 preState 是 undefined

传递的 action 是 {type: '@@redux/INITn.j.9.n.r.k'}

1.5 在 index.js 中检测 store 中状态的改变,一旦发生改变重新渲染

备注:redux 只负责管理状态,至于状态的改变驱动着页面的展示,要靠我们自己写

2、求和案例_redux 完整版

新增文件

1、count_action.js 专门用于 创建 action 对象

2、constant.js 放置由于编码疏忽写错 action 中的 type

3、求和案例_redux 异步 action 版

1、明确:延迟的动作不想交给组件自身,想交给 action

2、何时需要异步 action:想要状态进行操作,但是具体的数据靠异步任务返回(非必须)

3、具体编码:

3.1 yarn add redux-thunk, 并配置在 store 中

3.2 创建 action 的函数不再返回一般对象,而是一个函数,该函数中写异步任务

3.3 异步任务有结果后,分发一个同步的 action 去真正操作数据

4、备注:异步 action 不是必须要写的,完全可以自己等待异步任务的结果返回了再去分发同步 action

4、求和案例_react-redux 基本使用

4.1 明确两个概念

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

2、容器组件:负责和 redux 同学呢,将结果交给 UI 组件

4.2 如何创建一个容器组件------ react-redux 的 connect 函数

connect(mapStateToProps,mapDisapatchToProps)(UI 组件)

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

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

容器组件的写法:

jsx 复制代码
// 容器组件 react-redux

// 引入Count 的 UI 组件
import CountUI from '../../components/Count'
// 引入 action
import {
  createIncrementAction,
  createDecrementAction,
  createIncrementAsyncAction,
} from '../../redux/count_action'

// 引入 connect 用于连接 UI 组件与 redux
import { connect } from 'react-redux'

/**
 * 1、mapStateToProps 函数返回的是一个对象;
 * 2、返回的对象中的 key 就作为传递给 UI 组件 props 的key, value就作为传递给 UI 组件 props 的value
 * 3、mapStateToProps 用于传递状态
 */
// mapStateToProps 函数的返回值作为状态传递给了 UI 组件
// 返回的对象中的 key 就作为传递给 UI 组件 props 的key, value就作为传递给 UI 组件 props 的value -- 状态
const mapStateToProps = (state) => {
  return {
    count: state,
  }
}

// mapDisapatchToProps 函数返回的对象中的 key 就作为传递给 UI 组件 props 的key, value就作为传递给 UI 组件 props 的value -- 操作状态的方法
const mapDisapatchToProps = (dispatch) => {
  return {
    jia: (data) => {
      // 通知 redux 执行加法
      dispatch(createIncrementAction(data))
    },
    jian: (data) => {
      // 通知 redux 执行加法
      dispatch(createDecrementAction(data))
    },
    jiaAsync: (data) => {
      // 通知 redux 执行加法
      dispatch(createIncrementAsyncAction(data, 500))
    },
  }
}

// 使用 connect()() 创建并暴露一个 Count 的容器组件
export default connect(mapStateToProps, mapDisapatchToProps)(CountUI)

UI 组件的写法:

jsx 复制代码
import React, { Component } from 'react'
export default class CountUI extends Component {
  // 加法
  increment = () => {
    const { value } = this.selectNo
    this.props.jia(value * 1)
  }
  // 减法
  decrement = () => {
    const { value } = this.selectNo
    this.props.jian(value * 1)
  }
  // 当前求和为奇数再加
  incrementIfOdd = () => {
    let { count } = this.props
    if (count % 2 !== 0) {
      this.increment()
    }
  }
  //异步加
  incrementAsync = () => {
    const { value } = this.selectNo
    this.props.jiaAsync(value * 1)
  }
  render() {
    console.log('props--->', this.props)
    return (
      <div>
        <h1>当前求和为:{this.props.count}</h1>
        <select
          ref={(c) => {
            this.selectNo = c
          }}
        >
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>&nbsp;&nbsp;
        <button onClick={this.increment}>加</button> &nbsp;&nbsp;
        <button onClick={this.decrement}>减</button>&nbsp;&nbsp;
        <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>
        &nbsp;&nbsp;
        <button onClick={this.incrementAsync}>异步加</button>
      </div>
    )
  }
}

4.3 备注 1:容器组件中的 store 是靠 props 传递的,而不是容器组件中直接引入 store

4.4 备注 2:mapDispatchToProps 也可以是一个对象

react-redux 会将 action 进行分发,不再需要自己调用 dispatch

容器组件(自带监测功能)的优化写法:

jsx 复制代码
// 容器组件 react-redux

// 引入Count 的 UI 组件
import CountUI from '../../components/Count'
// 引入 action
import {
  createIncrementAction,
  createDecrementAction,
  createIncrementAsyncAction,
} from '../../redux/count_action'

// 引入 connect 用于连接 UI 组件与 redux
import { connect } from 'react-redux'

// 使用 connect()() 创建并暴露一个 Count 的容器组件
export default connect(
  (state) => ({ count: state }),
  /*
  // mapDispatchToProps 的一般写法
  dispatch=>{
    return  {
      jia: (data) =>{
          // 通知 redux 执行加法
        dispatch(createIncrementAction(data))
      },
      jian: (data) =>{
        // 通知 redux 执行加法
        dispatch(createDecrementAction(data))
      },
      jiaAsync: (data) =>{
        // 通知 redux 执行加法
        dispatch(createIncrementAsyncAction(data, 500))
      },
    }
  }
  */
  // mapDispatchToProps 的简写,
  {
    jia: createIncrementAction, // react-redux 会将action进行分发,不再需要自己调用 dispatch
    jian: createDecrementAction,
    jiaAsync: createIncrementAsyncAction,
  },
)(CountUI)

容器组件里面的 store 从哪里得到的?------

5、求和案例_react-redux 优化

1、容器组件和 UI 组件混成一个文件

2、无需自己给容器组件传递 store,给 包裹一个 Provider store={store} 即可

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

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

5、一个组件要和 redux "打交道"要警告过哪几步?

(1)定义 UI 组件 --- 不暴露

(2)引入 connect 生成一个容器组件并暴露,写法如下:

jsx 复制代码
connect(
  state=>(key:value),// 映射状态
  {key: xxxxAction}//映射操作状态的方法
)(UI 组件)

(3)在 UI 组件中通过 this.props.xxx 读取和操作状态

6、多组件共享数据_redux 管理

1、定义一个 Person 组件,和 Count 组件通过 redux 共享数据

2、为 Person 组件编写:reducer、action ,配置 constant 常量

3、重点:Person 的 reducer 和 Count 的 reducer 要使用 combineReducers 进行合并,合并后的总状态时一个对象!!!

4、交给 store 的时总 reducer,最后注意在组件中取出状态的时候,记得"取到位"

jsx 复制代码
export default connect(
  (state) => ({
    sum: state.sum,
    personList: state.personList,
  }),
  {
    jia: createIncrementAction,
  },
)(Count)

Count 组件的 action

jsx 复制代码
/**
 * 该文件专门为 Count 组件生成 action 对象
 */
import { INCREMENT, DECREMENT } from '../constant'
export const createIncrementAction = (data) => {
  return {
    type: INCREMENT,
    data,
  }
}
// 同步 action,返回一般对象。同步 action 的值为 Object 类型的一般对象
export const createDecrementAction = (data) => ({
  type: DECREMENT,
  data,
})
// 异步action ,返回 函数。 异步action 就是指 action 的值为函数。异步action 中一般都会调用同步 action
export const createIncrementAsyncAction = (data, time) => {
  return (dispatch) => {
    console.log(dispatch, 'dispatch')
    setTimeout(() => {
      dispatch(createIncrementAction(data))
    }, time)
  }
}
// 数组和数字都不能够开启 异步任务,所以不会定义这些类型为异步 action

7、纯函数

1、一类特别的函数:只要是同样的输入(实参),必定得到同样的输出(返回)

2、必须遵守以下一些约束

(1)不得改参数数据

(2)不会产生任何副作用,例如网络请求,输入和输出设备

(3)不能调用 Date.now() 或者 Math.random() 等不纯的方法

3、redux 的 reducer 函数必须是一个纯函数(不能写 unshift 等方法添加数据,如果 preState 被改写 reducer 就不纯了,页面也不会更新)!!!

8、Redux 开发者工具 redux-devtool

1、安装开发者工具

2、安装依赖(配合插件使用)

npm i redux-devtools-extension

3、store 中引入

jsx 复制代码
import { composeWithDevTools } from 'redux-devtools-extension'
export default createStore(
  allReducer,
  composeWithDevTools(applyMiddleware(thunk)),
)

store.js 完整代码

jsx 复制代码
/**
 * 该文件专门用于暴露一个store 对象,整个应用只有一个 store 对象
 */
// 引入createStore,专门用于创建 redux 中最为核心的是store 对象
import { applyMiddleware, createStore, combineReducers } from 'redux'

// 引入 redux-thunk 用于支持异步action
import thunk from 'redux-thunk'

// 引入为 Count 组件服务的reducer
import countReducer from './reducers/count'
import personReducer from './reducers/person'
import { composeWithDevTools } from 'redux-devtools-extension'

// 汇总所有的 reducer 变成一个总的 reducer
const allReducer = combineReducers({
  sum: countReducer,
  personList: personReducer,
})

export default createStore(
  allReducer,
  composeWithDevTools(applyMiddleware(thunk)),
)

五、项目打包运行

npm run build

使用 serve 插件可以在本地开启一个本地服务器,使用命令 serve 就可以启动当前的文件夹
npm i serve -g

项目资源文件

相关推荐
GISer_Jing2 小时前
前端面试通关:Cesium+Three+React优化+TypeScript实战+ECharts性能方案
前端·react.js·面试
落霞的思绪3 小时前
CSS复习
前端·css
咖啡の猫5 小时前
Shell脚本-for循环应用案例
前端·chrome
百万蹄蹄向前冲7 小时前
Trae分析Phaser.js游戏《洋葱头捡星星》
前端·游戏开发·trae
朝阳5817 小时前
在浏览器端使用 xml2js 遇到的报错及解决方法
前端
GIS之路8 小时前
GeoTools 读取影像元数据
前端
ssshooter8 小时前
VSCode 自带的 TS 版本可能跟项目TS 版本不一样
前端·面试·typescript
Jerry9 小时前
Jetpack Compose 中的状态
前端
dae bal10 小时前
关于RSA和AES加密
前端·vue.js
柳杉10 小时前
使用three.js搭建3d隧道监测-2
前端·javascript·数据可视化