Young Kbt blog Young Kbt blog
首页
  • java基础

    • Java基础
    • Java集合
    • Java反射
    • JavaJUC
    • JavaJVM
  • Java容器

    • JavaWeb
  • Java版本新特性

    • Java新特性
  • SQL 数据库

    • MySQL
    • Oracle
  • NoSQL 数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • ActiveMQ
    • RabbitMQ
    • RocketMQ
    • Kafka
  • 进阶服务

    • Nginx
  • Spring
  • Spring Boot
  • Spring Security
  • 设计模式
  • 算法
  • 知识
  • 管理

    • Maven
    • Git
  • 部署

    • Linux
    • Docker
    • Jenkins
    • Kubernetes
  • 进阶

    • TypeScript
  • 框架

    • React
    • Vue2
    • Vue3
  • 轮子工具
  • 项目工程
  • 友情链接
  • 本站

    • 分类
    • 标签
    • 归档
  • 我的

    • 收藏
    • 关于
    • Vue2-Admin (opens new window)
    • Vue3-Admin(完善) (opens new window)
GitHub (opens new window)

Shp Liu

朝圣的使徒,正在走向编程的至高殿堂!
首页
  • java基础

    • Java基础
    • Java集合
    • Java反射
    • JavaJUC
    • JavaJVM
  • Java容器

    • JavaWeb
  • Java版本新特性

    • Java新特性
  • SQL 数据库

    • MySQL
    • Oracle
  • NoSQL 数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • ActiveMQ
    • RabbitMQ
    • RocketMQ
    • Kafka
  • 进阶服务

    • Nginx
  • Spring
  • Spring Boot
  • Spring Security
  • 设计模式
  • 算法
  • 知识
  • 管理

    • Maven
    • Git
  • 部署

    • Linux
    • Docker
    • Jenkins
    • Kubernetes
  • 进阶

    • TypeScript
  • 框架

    • React
    • Vue2
    • Vue3
  • 轮子工具
  • 项目工程
  • 友情链接
  • 本站

    • 分类
    • 标签
    • 归档
  • 我的

    • 收藏
    • 关于
    • Vue2-Admin (opens new window)
    • Vue3-Admin(完善) (opens new window)
GitHub (opens new window)
  • 超文本标记语言 - Html

  • 解释编程语言 - JavaScript

  • JS 超集语言 - TypeScript

  • 界面构建框架 - React

    • React - 基础与核心
    • React - 脚手架及使用
    • React - 路由
    • React - Redux
      • Redux理解
      • 什么情况下需要使用Redux
      • Redux的三个核心概念
        • action
        • reducer
        • store
      • 组件与reducer交互
      • 组件与同步action、reducer交互
      • 异步action
      • Redux和React-Redux区别
      • React-Redux基本使用
      • React-Redux优化
      • React-Redux数据共享
      • React-Redux开发者工具的使用
      • React-Redux最终优化
    • React - 函数类型
    • React - 进阶知识
    • React - 父子传值与循环key
  • 渐进式框架 - Vue2

  • 渐进式框架 - Vue3

  • 前端
  • 界面构建框架 - React
Young Kbt
2022-02-21
目录

React - Redux

  • Redux理解
  • 什么情况下需要使用Redux
  • Redux的三个核心概念
    • action
    • reducer
    • store
  • 组件与reducer交互
  • 组件与同步action、reducer交互
  • 异步action
  • Redux和React-Redux区别
  • React-Redux基本使用
  • React-Redux优化
  • React-Redux数据共享
  • React-Redux开发者工具的使用
  • React-Redux最终优化

# Redux理解

  1. Redux 是一个专门用于做 状态管理的 JS 库(不是 React 插件库)

  2. 它可以用在 React、Angular、Vue 等项目中,但基本与 React 配合使用

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

# 什么情况下需要使用Redux

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

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

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

# Redux的三个核心概念

# action

  1. 动作的对象

  2. 包含两个属性

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

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

  3. 例子:{type:'ADD',data:{name:'kele',age:18}}

# reducer

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

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

# store

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

  2. 如何得到此对象?

    // 引入 createStore,专门用于创建 Redux 中最为核心的 store 对象
    import {createStore} from 'Redux'
    // 引入为 Count 组件服务的 reducer
    import reducer from './reducers'
    const store = createStore(reducer)
    export default store
    
    1
    2
    3
    4
    5
    6
  3. 此对象的功能

    • getState(): 得到 state

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

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

# 组件与reducer交互

  1. 去除 Count 组件自身的状态

  2. src 下建立:

    -Redux
    -store.js
    -count_reducer.js	//xxx_reducer代表xxx组件的reducer
    
    1
    2
    3
  3. store.js:

    • 引入 Redux 中的 createStore 函数,创建一个 store
    • createStore 调用时要传入一个为其服务的 reducer
    • 记得暴露 store 对象
    import {createStore} from 'Redux'
    // 引入为 Count 组件服务的 reducer
    import reducer from './reducers'
    // 暴露 store
    export default createStore(reducer)
    
    1
    2
    3
    4
    5
  4. 组件的使用

    • 引入 store,用于获取 Redux 中保存状态

    • 使用 store 的 API:dispatch 方法,直接传入 reducer 中加工

      参数一:type,reducer 中关联的函数名(需要触发的函数)

      参数二:data,操作的数据

    increment = ()=>{
        store.dispatch(type:'increment',data:value*1))
    }
    
    1
    2
    3
  5. reducer.js:

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

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

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

      • 传递的 preState 是 undefined

      • 传递的 action 是:{type:'@@REDUX/INIT_a.2.b.4}

      • 后面通过 dispatch 传入的 preState 为 Redux 的 state 的数据,action 是要执行数据的行为

    • 技巧:开头定义一个常量,给 preState 指定该默认值,当 preState 为 undefined,返回该常量

    /* 
       1. 该文件是用于创建一个为 Count 组件服务的 reducer,reducer 的本质就是一个函数
       2. reducer 函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
    */
    const initState = 0 // 初始化状态
    export default function countReducer(preState=initState,action){
       // 从 action 对象中获取:type、data
       const {type,data} = action
       // 根据 type 决定如何加工数据
       switch (type) {
       	case 'increment': // 如果是加
       		return preState + data
       	case 'decrement': // 若果是减
       		return preState - data
       	default:
       		return preState
       }
    } 
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
  6. 在 index.js 中监测 store 中状态的改变,一旦发生改变重新渲染 <App/>

    备注:Redux 只负责管理状态,至于状态的改变(this.setState={})驱动着页面的展示,要靠我们自己写。

    ReactDOM.render(<App/>,document.getElementById('root'))
    // 监测 Redux 中状态的改变,如 Redux 的状态发生了改变,那么重新渲染 App 组件
    store.subscribe(()=>{
    	ReactDOM.render(<App/>,document.getElementById('root'))
    })
    
    1
    2
    3
    4
    5

# 组件与同步action、reducer交互

  1. action.js:专门用于创建 action 对象,对 dispatch 传入的值进行封装,固定 type,只需传入 data

  2. 细节:如果使用箭头函数简写模式,return 的时对象,需要使用括号 (),否则默认 {} 是函数形式而不是对象形式

    /* 
    	该文件专门为 Count 组件生成 action 对象
    */
    export const createIncrementAction = data => ({type:'increment',data})
    export const createDecrementAction = data => ({type:'decrement',data})
    
    1
    2
    3
    4
    5
  3. 原先组件的 dispatch 变为

    import {createIncrementAction} from './action'
    store.dispatch(createIncrementAction(value*1))
    
    1
    2
  4. constant.js:当出现多个常量时,此文件放置 type 值在 Redux 文件夹里

    /* 
    	该模块是用于定义 action 对象中 type 类型的常量值,目的只有一个:便于管理的同时防止程序员单词写错
    */
    export const INCREMENT = 'increment'
    export const DECREMENT = 'decrement'
    
    1
    2
    3
    4
    5
  5. 此时其他文件的对用常量变为

    // reducer 文件
    ......
    switch (type) {
        case INCREMENT: // 如果是加
            return preState + data
        case DECREMENT: // 若果是减
            return preState - data
        default:
            return preState
    }
    ....
    // action 文件:
    export const createIncrementAction = data => ({type:INCREMENT,data})
    export const createDecrementAction = data => ({type:DECREMENT,data})
    ......
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

# 异步action

同步 action 和异步 action 区别:前者返回值是一个对象,后者返回值是一个函数,因为只有函数才能开启异步,如定时器

使用方式:(引入中间库)

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

  • 何时需要异步 action:想要对状态进行操作,但是具体的数据靠异步任务返回

  • 具体编码:

    1. yarn add Redux-thunk,并配置在 store 中

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

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

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

  • 异步 action 一定会 dispatch 同步 action

代码

store.js:引入中间件,并且在 createStore 时使用

// 引入 createStore,专门用于创建 Redux 中最为核心的 store 对象
import {createStore,applyMiddleware} from 'Redux'
// 引入为 Count 组件服务的 reducer
import countReducer from './count_reducer'
// 引入 Redux-thunk,用于支持异步 action
import thunk from 'Redux-thunk'
// 暴露 store
export default createStore(countReducer,applyMiddleware(thunk))
1
2
3
4
5
6
7
8

action.js:使用异步 action

import {INCREMENT,DECREMENT} from './constant' // 常量文件名

// 同步a ction,就是指 action 的值为 Object 类型的一般对象
export const createIncrementAction = data => ({type:INCREMENT,data})
export const createDecrementAction = data => ({type:DECREMENT,data})

// 异步 action,就是指 action 的值为函数,异步 action 中一般都会调用同步 action,异步 action 不是必须要用的。
export const createIncrementAsyncAction = (data,time) => {
	return (dispatch)=>{
		setTimeout(()=>{
			dispatch(createIncrementAction(data))
		},time)
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

记住:如果不安装并且使用中间件,action.js 里的异步 action 代码会 报错。

# Redux和React-Redux区别

  1. Redux 是 React 官方团队出品,React-Redux 是 Facebook 官方团队出品
  2. React-Redux 对 Redux 进行再次封装,解耦更明显,分离都明显,功能更齐全
  3. 使用了 React-Redux,则不需要自己主动监测状态变化,页面渲染
  4. 使用了 React-Redux,UI组件不能写任何 Redux 的内容,必须使用其父容器组件传来的值(父组件才写 Redux 的内容,并且响应事件,通过 props 传给其子 UI 组件)

# React-Redux基本使用

  1. 两个概念:(React-Redux 对使用 Redux 的组件进行再次封装)

    • UI 组件:不能使用任何 Redux 的 api,只负责页面的呈现、交互等。放在 components 文件夹

    • 容器组件:负责和 Redux 通信,将结果交给 UI 组件。放在 contains 文件夹

  2. 如何创建一个容器组件?靠 React-Redux 的 connect 函数

    格式:

    // 使用 connect()() 创建并暴露一个 Count 的容器组件
    connect(mapStateToProps,mapDispatchToProps)(UI组件)
    
    1
    2

    左边括号:两个参数,且参数都必须为函数,右边括号:UI 组件的名称,需 import 引入。

    mapStateToProps:映射状态,返回值是一个对象,是一个传递状态(数据)的函数,给 UI 组件。

    mapDispatchToProps:映射操作状态的方法,返回值是一个对象,是一个传递操作状态的方法的函数,或者对象,给 reducer 加工。

    流程:UI 组件触发事件,事件通过 mapDispatchToProps 传入 reducer 进行加工,然后更新状态,mapStateToProps 通过 props 传更新的状态给 UI 组件,UI 组件直接获取。

    备注:容器组件中的 store 是靠 props 传进去的,而不是在容器组件中直接引入(App 是 Count 的父组件,Count 组件有容器组件和 UI 组件),然后 App 组件传 store 给 Count 容器组件,Count 容器组件进行操作后数据传给 CountUI 组件

创建容器组件

App 组件

......
export default class App extends Component {
	render() {
		return (
			<div>
				{/* 给容器组件传递store */}
				<Count store={store} />
			</div>
		)
	}
}
.....
1
2
3
4
5
6
7
8
9
10
11
12

Count 容器组件(获取父组件传入的 store,并且使用 dispatch 对 Redux 进行数据加工,后返回给 CountUI 组件)

// 引入 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 用于传递状态
*/
function mapStateToProps(state){
	return {count:state}
}

/* 
	1.mapDispatchToProps 函数返回的是一个对象;
	2.返回的对象中的 key 就作为传递给 UI 组件 props 的 key,value 就作为传递给 UI 组件 props 的 value
	3.mapDispatchToProps 用于传递操作状态的方法
*/
function mapDispatchToProps(dispatch){
	return {
		increment:number => dispatch(createIncrementAction(number)),
		decrement:number => dispatch(createDecrementAction(number)),
		incrementAsync:(number,time) => dispatch(createIncrementAsyncAction(number,time)),
	}
}

// 使用 connect()() 创建并暴露一个 Count 的容器组件
export default connect(mapStateToProps,mapDispatchToProps)(CountUI)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

CountUI 组件(直接使用 props 发送给父组件自己的事件和参数,然后父组件们执行完操作,重新渲染画面)

export default class Count extends Component {
    //加法
    increment = ()=>{
        const {value} = this.selectNumber
        this.props.increment(value*1)
    }
    ... //减法、异步加法
    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>&nbsp;
				<button onClick={this.increment}>+</button>&nbsp;
     			......{/* 减法、异步加法的按钮和事件 */}           
			</div>
		)
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

优化:mapDispatchToProps,也可以是一个对象,看下方代码块的简写:

分析:this.props.increment(value * 1) 调用对象的 key:increment,随后使用 createIncrementAction 函数,并且因为调用的时候传入参数 value * 1,所以把该参数传入 createIncrementAction 函数,该函数随后返回 action 对象,dispatch 方法是 API 自动加入,已经优化

export default connect(
	state => ({count:state}),

	// mapDispatchToProps 的一般写法
	/* dispatch => ({
		increment:number => dispatch(createIncrementAction(number)),
		decrement:number => dispatch(createDecrementAction(number)),
		incrementAsync:(number,time) => dispatch(createIncrementAsyncAction(number,time)),
	}) */

	// mapDispatchToProps 的简写
	{
		increment:createIncrementAction,
		decrement:createDecrementAction,
		incrementAsync:createIncrementAsyncAction,
	}
)(Count)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# React-Redux优化

容器组件和 UI 组件整合一个文件

  • 因为容器组件需要引入 UI 组件,然后 connect(a,b)(UI组件),为何不直接放在一起,然后无需引入 UI 组件,直接整合一个文件

  • 整合一个文件后,只需 export default connect(a,b)(UI组件)即可,UI 组件因为都在一个文件,内部直接使用

无需自己给容器组件传递 store,给 <App/> 包裹一个 <Provider store={store}> 即可。目的是让 App 所有的后代容器组件都能接收到 store。

  • 原先不优化(在 App 组件)

    render() {
        return (
            <div>
                <Count store={store}/>
                <Count store={store}/>
                <Count store={store}/>
                <Count store={store}/>
                <Count store={store}/>
            </div>
        )
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  • 优化后(入口文件)

    ......
    import {Provider} from 'React-Redux'
    ReactDOM.render(
        /* 此处需要用Provider包裹App,目的是让App所有的后代容器组件都能接收到store */
        <Provider store={store}>
            <App/>
        </Provider>,
        document.getElementById('root')
    )
    // 此时App文件为
    render() {
        return (
            <div>
                <Count/>
                <Count/>
                <Count/>
                <Count/>
            </div>
        )
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

使用了 React-Redux 后不用再自己检测 Redux 中状态的改变了,容器组件可以自动完成这个工作。下方代码(入口文件里)可以 去掉

// 监测 Redux 中状态的改变,如 Redux 的状态发生了改变,那么重新渲染 App 组件
store.subscribe(()=>{
	ReactDOM.render(<App/>,document.getElementById('root'))
})
1
2
3
4

mapDispatchToProps 也可以简单的写成一个对象,具体内容看 React-Redux 基本使用。

一个组件要和 Redux「打交道」要经过哪几步?(融合 1、3、4 的内容)

  • 定义好 UI 组件,不暴露

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

export default connect(
    state => ({key:value}), // 映射状态
    {key:xxxxxAction} // 映射操作状态的方法,尽量触发简写形式
)(UI组件)
1
2
3
4

在 UI 组件中通过 this.props.xxx 读取和操作状态,即调用上方(5)第三行的 key。

# React-Redux数据共享

  1. 定义一个 Pserson 组件,和 Count 组件通过 Redux 共享数据

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

  3. 重点:Person 的 reducer 和 Count 的 Reducer 要使用 combineReducers 进行合并,需要引入合并后的总状态是一个对象

    // 引入 createStore,专门用于创建Redux中最为核心的store对象
    import {createStore,applyMiddleware,combineReducers} from 'Redux'
    // 引入为 Count 组件服务的 reducer
    import countReducer from './reducers/count'
    // 引入 为 Count 组件服务的 reducer
    import personReducer from './reducers/person'
    // 引入 Redux-thunk,用于支持异步 action
    import thunk from 'Redux-thunk'
    
    // 汇总所有的 reducer 变为一个总的 reducer
    const allReducer = combineReducers({
        countReducer:countReducer,
        countReducer:personReducer
    })
    
    // 暴露 store
    export default createStore(allReducer,applyMiddleware(thunk))
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
  4. 交给 store 的是总 reducer(对象),最后注意在组件中取出状态(state)的时候,不再直接是 state,而是 state.countReducer 和 state.personReducer。如果交给 store 的是一个 value 值,通过 state 取这个 value 值,对象的话通过 state.xxx 取 key 得 value

# React-Redux开发者工具的使用

  1. 使用开发者工具需要安装第三方库支持:(去 chrome 扩展商城下载)

    yarn add Redux-devtools-extension
    
    1
  2. store 中进行配置

    import {composeWithDevTools} from 'Redux-devtools-extension'
    const store = createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))
    // 如果没有引入 Redux-thunk
    const store = createStore(allReducer,composeWithDevTools())
    
    1
    2
    3
    4

# React-Redux最终优化

  • 所有变量名字要规范,尽量触发对象的简写形式。

  • reducers 文件夹中,编写 index.js 专门用于汇总并暴露所有的 reducer

  • 状态如果是一个数组,对该数组进行操作,不能使用 unshift,push 等方法,这样无法改变数组本身的地址,从而数据无法被监测到,实现更新。Vue 框架则相反,必须使用 unshift,push 等方法,才能监测到数据的改变。

编辑此页 (opens new window)
#React
更新时间: 2023/09/18, 16:34:13
React - 路由
React - 函数类型

← React - 路由 React - 函数类型→

最近更新
01
技术随笔 - Element Plus 修改包名 原创
11-02
02
Reactor - 扩展性
11-02
03
Reactor - 最佳实践
11-02
更多文章>
Theme by Vdoing | Copyright © 2021-2024 Young Kbt | blog
桂ICP备2021009994号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式