文章目录
- 1 redux
- 1.1 概述
- 1.2 示例实现页面数字的加、减
- 1.3 异步加-异步action
- 2 react-redux
- 2.1 概述
- 2.2 优化示例代码
- 3 数据共享
- 4 redux开发者工具
- 5 小结
- 5.1 求和案例_redux精简版
- 5.2 求和案例_redux完整版
- 5.3 求和案例_redux异步action版
- 5.4 求和案例_react-redux基本使用
- 5.5 求和案例_react-redux优化
- 5.6 求和案例_react-redux数据共享版
- 5.7 求和案例_react-redux开发者工具的使用
- 5.8 求和案例_react-redux最终版
- 结语
1 redux
1.1 概述
Redux是一个用于JavaScript应用程序的状态管理库。它被设计用于管理应用程序的状态,使得状态的变化可预测且易于理解。
Redux的核心概念是单一的状态树(single state tree)。应用程序的整个状态被存储在一个对象中,称为"store"。这个状态树是只读的,意味着它不能直接修改。如果想要改变应用程序的状态,需要触发一个"action",描述状态的变化,并且通过一个特定的函数(称为"reducer")来处理这个action,并返回一个新的状态。
三个概念:
-
store:store是一个仓库,用来存储数据,它可以获取数据,也可以派发数据,还能监听到数据的变化。
-
action:action理解为动作,action的值一般为一个对象,格式如 { type: “”, data: “” },type是必须要的,因为reducer处理数据的时候要根据不同的type来进行不同的操作。
-
reducer:reducer是初始化以及处理派发的action的纯函数。
Redux的工作流程如下:
- 应用程序的状态被存储在一个单一的store中。
- 当应用程序的状态发生变化时,触发一个action。
- action被发送到reducer,根据action的类型来更新状态。
- reducer返回一个新的状态,替换原始的状态树。
- 视图根据新的状态重新渲染。
Redux的设计理念是可预测性和可测试性。通过将状态集中管理,我们可以更容易地追踪和调试状态的变化,而不需要在应用程序的各个部分传递回调函数或事件处理程序。
Redux并不是React特定的库,它可以与任何JavaScript框架或库一起使用。然而,Redux与React结合使用非常流行,因为它提供了一种简单和可预测的方式来管理React应用程序的状态。为了与React更好地集成,Redux提供了一个React的官方绑定库称为"React Redux",简化了Redux在React应用程序中的使用。
总结一下,Redux是一个用于JavaScript应用程序的状态管理库,通过集中管理状态,提供了可预测和可测试的方式来管理应用程序的状态变化。它可以与各种前端框架或库一起使用,但最常与React一起使用。
1.2 示例实现页面数字的加、减
创建计数组件Count ,index.jsx代码如下1.2-1所示:
import React, { Component } from 'react'
import store from '../../redux/store'
import { incrementAction, decrementAction} from "../../redux/count_action";
export default class Count extends Component {
componentDidMount() {
// 检测redux中状态变化,调用render
store.subscribe(() => {
this.setState({})
})
}
/**
* 加法
*/
increment = () => {
// 获取选中值
const { value } = this.selectNumber
store.dispatch(incrementAction(value - 0))
}
/**
* 减法
*/
decrement = () => {
// 获取选中值
const { value } = this.selectNumber
store.dispatch(decrementAction(value - 0))
}
/**
* 异步加
*/
incrementAsync = () => {
// 获取选中值
const { value } = this.selectNumber
setTimeout(() => {
store.dispatch(incrementAction(value - 0, 500))
}, 500);
}
render() {
return (
<div>
<h1>当求和为:{store.getState()}</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.incrementAsync}>异步加</button>
</div>
)
}
}
src/redux/store.js,源代码1.2-2如下所示:
/**
* 该文件用于暴露store对象,整个应用只有1个store对象
*/
// 引入createStore,创建store对象
import { createStore } from "redux";
// 引入为Count组件服务的reducer
import countReducer from './count_reducer'
// 暴露store对象
export default createStore(countReducer)
src/redux/count_reducer.js,源代码如下1.2-3所示:
/**
* 文件用于创建为Count组件服务的reducer
*/
import { INCREMENT, DECREMENT } from "./constant";
export default function countReducer(state = 0, action) {
console.log('---', state, '----', action);
// 从action中获取type,data
const { type, data } = action
// 根据类型决定如何加工数据
switch (type) {
case INCREMENT:
return state + data
break;
case DECREMENT:
return state - data
default:
return state
break;
}
}
src/redux/count_action.js,源代码1.2-4如下所示:
/**
* Count组件action对象
*/
import { INCREMENT, DECREMENT } from "./constant";
// 同步action
export const incrementAction = data => ({type: INCREMENT, data})
export const decrementAction = data => ({type: DECREMENT, data})
src/redux/constant.js,常量,源代码如下1.2-5所示:
/**
* 定义action对象中type类型的常量
*/
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement
1.3 异步加-异步action
我们当前执行的异步加,通过定时器,组件自己等待完成的。但是我们不想等,需要借助异步action,交给redux。
- 同步action:返回为Object:{}对象为同步action
- 异步action:返回function函数为异步action
改造Count/index.jsx,异步加调用
store.dispatch(incrementActionAsync(value - 0, 500))
src/redux/count_action.js中添加incrementActionAsync方法,代码如下
// 异步action,指action的值为
export const incrementActionAsync = (data, time) => {
return (dispatch) => {
setTimeout(() => {
dispatch(incrementAction(data))
}, time);
}
}
store接收到funciton类型的action,会报错
Actions must be plain objects. Instead, the actual type was: 'function'. You may need to add middleware to your store setup to handle dispatching other values, such as 'redux-thunk' to handle dispatching functions.
此时根据提示,需要借助redux-thunk库,安装库文件
yarn add redux-thunk
安装完成后,在src/redux/store.js中引入
/**
* 该文件用于暴露store对象,整个应用只有1个store对象
*/
// 引入createStore,创建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-1所示:
2 react-redux
2.1 概述
React Redux是Redux官方提供的React绑定库,用于在React应用程序中集成和使用Redux。它提供了一些特定的组件和API,简化了在React中使用Redux的过程。
React Redux的核心概念是将Redux的store中的状态映射到React组件的props,并将React组件中的操作(如用户交互)映射为Redux的actions,从而实现状态管理和数据流的一致性。
React Redux提供了两个重要的组件:
<Provider>
:这是一个高阶组件(Higher Order Component),用于将Redux的store传递给React应用程序的组件树。通过在应用程序的根组件中使用<Provider>
,Redux的store将成为应用程序中的全局状态,所有子组件都可以访问并使用这个状态。connect()
:这是一个函数,用于连接React组件与Redux的store。通过connect()
,可以指定哪些状态需要从Redux的store中获取,并将其映射为组件的props。它还允许将Redux的actions绑定到组件中的操作,并将它们分发到Redux的store中。connect()
函数的返回值是一个增强后的组件,包含了与Redux的store进行交互的功能。
使用React Redux时,首先需要在应用程序的根组件中使用<Provider>
将Redux的store传递给组件树。然后,在需要访问Redux状态或分发actions的组件中,使用connect()
函数来连接Redux的store。
2.2 优化示例代码
- 整合UI组件和容器组件
src/containers/Count/index.jsx,源代码如下2.2-1所示:
import React, { Component } from 'react'
// 引入connect用于连接UI组件和redux
import { connect } from "react-redux";
// 引入action
import { incrementAction, decrementAction, incrementActionAsync } from "../../redux/count_action";
// 定义UI组件
class Count extends Component {
/**
* 加法
*/
increment = () => {
// 获取选中值
const { value } = this.selectNumber
this.props.increment(value - 0)
}
/**
* 减法
*/
decrement = () => {
// 获取选中值
const { value } = this.selectNumber
this.props.decrement(value - 0)
}
/**
* 异步加
*/
incrementAsync = () => {
// 获取选中值
const { value } = this.selectNumber
this.props.incrementAsync(value - 0, 500)
}
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.incrementAsync}>异步加</button>
</div>
)
}
}
// 使用connect()() 创建并暴露一个Count容器组件
export default connect(
state => ({ count: state }), // 映射状态
{
// 映射操作状态方法
increment: incrementAction,
decrement: decrementAction,
incrementAsync: incrementActionAsync
}
)(Count)
App.jsx 源代码2.2-2如下所示:
import React, { Component } from 'react'
import Count from './containers/Count'
import store from './redux/store'
export default class App extends Component {
render() {
return (
<div>
<Count />
</div>
)
}
}
src/index.js监听状态改变,源代码2.2-3如下所示
import { Provider } from "react-redux";
// 引入App组件
import App from './App'
import store from "./redux/store";
// 渲染组件到页面
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
<Provider store={store}>
<App />
</Provider>
)
3 数据共享
上面演示了单个组件的中redux的使用,现在我们演示下两个(多个)组件如何实现数据共享。
效果如下图3-1所示:
参照Count组件内容,constants.js中增加 加人动作类型
/**
* 定义action对象中type类型的常量
*/
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
export const ADD_PERSON = 'add_person'
src/redux/actions/person.js 中增加addPerson
import { ADD_PERSON } from "../constant";
export const addPerson = data => ({type: ADD_PERSON, data})
src/redux/reducers/person,增加相应动作处理
/**
* 文件用于创建为Persion组件服务的reducer
*/
import { ADD_PERSON } from "../constant";
export default function addPerson(state = [], action) {
// 从action中获取type,data
const { type, data } = action
// 根据类型决定如何加工数据
switch (type) {
case ADD_PERSON:
return [data, ...state]
break;
default:
return state
break;
}
}
src/redux/reducers/index.js用于汇总所有的reducer
/**
* 该文件用于汇总所有的reducer
*/
// 引入createStore,创建store对象
import { combineReducers } from "redux";
// 引入为Count组件服务的reducer
import count from './count'
// 引入为Persion组件服务的reducer
import persons from './person'
// 汇总所有reducer
export default combineReducers({
count, persons
})
src/redux/store.js,中做相应修改
/**
* 该文件用于暴露store对象,整个应用只有1个store对象
*/
// 引入createStore,创建store对象
import { createStore, applyMiddleware } from "redux";
// 引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'
// 引入reducers
import reducers from './reducers';
// 暴露store对象
export default createStore(reducers, applyMiddleware(thunk))
src/containers/Person/index.jsx ,源代码如下所示:
import React, { Component } from 'react'
import { nanoid } from "nanoid";
// 引入connect用于连接UI组件和redux
import { connect } from "react-redux";
// 引入action
import { addPerson } from "../../redux/actions/person";
// 定义UI组件
class Person extends Component {
/**
* 添加人员
*/
addPerson = () => {
const name = this.nameNode.value
const age = this.ageNode.value
const person = {id: nanoid(), name, age}
this.props.addPerson(person)
this.nameNode.value = ''
this.ageNode.value = ''
}
render() {
return (
<div>
<h2>我是Persion组件, count为:{this.props.count}</h2>
<input ref={c => this.nameNode = c} type="text" placeholder='输入姓名' />
<input ref={c => this.ageNode = c} type="text" placeholder='输入年龄' />
<button onClick={this.addPerson}>添加</button>
<ul>
{
this.props.persons.map( p => {
return (
<li key={p.id}> 姓名:{p.name}----年龄:{p.age}</li>
)
})
}
</ul>
</div>
)
}
}
// 使用connect()() 创建并暴露一个Count容器组件
export default connect(
state => ({ persons: state.persons, count: state.count }), // 映射状态
{
// 映射操作状态方法
addPerson,
}
)(Person)
4 redux开发者工具
第一步:在chrome 插件库中安装Redux DevTools插件
第二步:安装对应的库文件
yarn add redux-devtools
第三步:store.js中使用
/**
* 该文件用于暴露store对象,整个应用只有1个store对象
*/
// 引入createStore,创建store对象
import { createStore, applyMiddleware } from "redux";
// 引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'
// 引入redux-devtools-extension
import { composeWithDevTools } from "redux-devtools-extension";
// 引入reducers
import reducers from './reducers';
// 暴露store对象
export default createStore(reducers, composeWithDevTools(applyMiddleware(thunk)))
功能使用如下图4-1所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZADMrs5W-1686152168535)(/Users/gaogzhen/baiduSyncdisk/study/front/react/note/08redux/images/截屏2023-06-07 23.00.12-redux-devtools-redux-react.png)]
5 小结
5.1 求和案例_redux精简版
(1).去除Count组件自身的状态
(2).src下建立:
-redux
-store.js
-count_reducer.js
(3).store.js:
1).引入redux中的createStore函数,创建一个store
2).createStore调用时要传入一个为其服务的reducer
3).记得暴露store对象
(4).count_reducer.js:
1).reducer的本质是一个函数,接收:preState,action,返回加工后的状态
2).reducer有两个作用:初始化状态,加工状态
3).reducer被第一次调用时,是store自动触发的,
传递的preState是undefined,
传递的action是:{type:'@@REDUX/INIT_a.2.b.4}
(5).在index.js中监测store中状态的改变,一旦发生改变重新渲染<App/>
备注:redux只负责管理状态,至于状态的改变驱动着页面的展示,要靠我们自己写。
5.2 求和案例_redux完整版
新增文件:
1.count_action.js 专门用于创建action对象
2.constant.js 放置容易写错的type值
5.3 求和案例_redux异步action版
(1).明确:延迟的动作不想交给组件自身,想交给action
(2).何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回。
(3).具体编码:
1).yarn add redux-thunk,并配置在store中
2).创建action的函数不再返回一般对象,而是一个函数,该函数中写异步任务。
3).异步任务有结果后,分发一个同步的action去真正操作数据。
(4).备注:异步action不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步action。
5.4 求和案例_react-redux基本使用
(1).明确两个概念:
1).UI组件:不能使用任何redux的api,只负责页面的呈现、交互等。
2).容器组件:负责和redux通信,将结果交给UI组件。
(2).如何创建一个容器组件————靠react-redux 的 connect函数
connect(mapStateToProps,mapDispatchToProps)(UI组件)
-mapStateToProps:映射状态,返回值是一个对象
-mapDispatchToProps:映射操作状态的方法,返回值是一个对象
(3).备注1:容器组件中的store是靠props传进去的,而不是在容器组件中直接引入
(4).备注2:mapDispatchToProps,也可以是一个对象
5.5 求和案例_react-redux优化
(1).容器组件和UI组件整合一个文件
(2).无需自己给容器组件传递store,给<App/>包裹一个<Provider store={store}>即可。
(3).使用了react-redux后也不用再自己检测redux中状态的改变了,容器组件可以自动完成这个工作。
(4).mapDispatchToProps也可以简单的写成一个对象
(5).一个组件要和redux“打交道”要经过哪几步?
(1).定义好UI组件---不暴露
(2).引入connect生成一个容器组件,并暴露,写法如下:
connect(
state => ({key:value}), //映射状态
{key:xxxxxAction} //映射操作状态的方法
)(UI组件)
(4).在UI组件中通过this.props.xxxxxxx读取和操作状态
5.6 求和案例_react-redux数据共享版
(1).定义一个Pserson组件,和Count组件通过redux共享数据。
(2).为Person组件编写:reducer、action,配置constant常量。
(3).重点:Person的reducer和Count的Reducer要使用combineReducers进行合并,
合并后的总状态是一个对象!!!
(4).交给store的是总reducer,最后注意在组件中取出状态的时候,记得“取到位”。
5.7 求和案例_react-redux开发者工具的使用
(1).yarn add redux-devtools-extension
(2).store中进行配置
import {composeWithDevTools} from 'redux-devtools-extension'
const store = createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))
5.8 求和案例_react-redux最终版
(1).所有变量名字要规范,尽量触发对象的简写形式。
(2).reducers文件夹中,编写index.js专门用于汇总并暴露所有的reducer
结语
❓QQ:806797785
⭐️源代码仓库地址:https://github.com/gaogzhen/react-staging.git
参考:
[1]React视频教程[CP/OL].2020-12-15.p97-115.
[2]React官网[CP/OL].