React学习05 - redux

ops/2024/11/18 22:24:53/

文章目录

  • redux工作流程
    • redux理解
    • redux理解及三个核心概念
    • redux核心api
    • redux异步编程
    • react-redux
      • 组件间数据共享
    • 纯函数
    • redux调试工具
    • 项目打包

redux工作流程

redux理解

redux是一个专门用于状态管理的JS库,可以用在react, angular, vue 等项目中。在与react配合使用时,集中式管理react应用中多个组件共享的状态。
当某个组件的状态需要让其他组件随时可以拿到时,当一个组件需要改变另一个组件的状态时,可以使用redux。
为什么不用消息订阅与发布?消息订阅与发布需要组件挂载后才能发布获取消息,当想要不挂载组件,先发布消息,再挂载组件接收消息的时候,就需要redux。
在react中使用redux,需要使用Redux Toolkit(RTK)和react-redux。

redux理解及三个核心概念

redux原理图

根据原理图,例如,当页面需要将一个数据+2,当前该数据为0 。

  1. Action Creators会创建一个action,把这个action分发(dispatch)给store,我们给action执行的操作自命名为Add,操作的数据为2。
  2. store接收到action后,将当前的状态0,和action一起交给Reducers,Reducers在原状态0的基础上,执行操作Add,也就是+2,然后返回新状态数据2 。
  3. Store接收到新状态,页面组件可以通过getState方法获取新状态数据。

当页面初次加载时,是在Reducers内初始化状态的,原状态为undefined,操作为初始化,Reducers可以初始化和加工状态。
三个核心概念:action, reducer, store。
action 包含两个属性,1.type 标识性属性,唯一且必要,值为字符串,2.data 数据属性,可选,值为任意类型。每个组件都可以有自己的ActionCreator。

{type:'自定义操作名称', data:数据} 

reducer是一个函数,根据旧的state和action产生新的state。每个组件都可以有自己的reducer。
store是一个对象,将state, action, reducer联系在一起。

import {createStore} from 'redux'
import reducer from './reducers'
const store = createStore(reducer)  // 获取store对象

通常集中状态管理的部分会在src下单独创建一个store目录,当建立很多子store模块时,在store目录下创建一个modules文件夹,包含所有子store模块文件,在store文件夹的index.js中将所有子模块组合并导出store。

redux核心api

通过案例代码理解store三个核心API: getState, dispatch, subscribe。在src下新建目录redux,创建store.js和demo_reducer.js两个文件,终端增加redux包。

npm add redux

redux4.0.5

// store.js
// 引入创建store的方法
import {createStore} from 'redux'
// 引入为组件服务的reducer
import demoReducer from './demo_reducer'
// 创建并暴露store
const store = createStore(demoReducer)
export default store// demo_reducer.js  为Demo组件服务的reducer,本质是函数,返回新状态
const initState = 0; // 初始化状态
export default function demoReducer(preState=initState,action){const {type,data} = action;switch(key){case 'increment':return preState + data;case 'decrement':return preState - data;default:return 0; //无指定操作,即为初始化状态操作}
}// Demo组件
import store from '../../redux/store'
export default class Demo extends Component{componentDisMount(){// 检测redux的状态更新,当前页面的状态随redux中的更新store.subscribe(()=>{this.setState({})})}add = ()=>{store.dispatch({type:'increment',data:2})}render(){return (<div><h1>当前值:{store.getState()}</h1><button onClick={this.add}>2</button></div>)}
}// App index.js Demo组件的subscribe可以去掉,放到App里,避免多个组件需要订阅重复代码
store.subscribe(()=>{ReactDOM.render(<App/>,document.getElementById('root'))
})

以上就完成了一个精简的redux。下面增加ActionCreator,将redux完善。在redux目录下创建demo_action.js。对应修改组件里调用dispatch。

// demo_action.js
export default function createIncrementAction(data){return {type:'increment',data}
}// Demo组件
// 引入actionCreator,专门用于创建action对象
import createIncrementAction from '../../redux/demo_action'
// add方法改为
add = ()=>{store.dispatch(createIncrementAction(2))
}

然后在redux目录下新建constant.js,用来将操作名集中管理,避免写错字符串。对应的修改reducer和action里的操作名。

// constant.js
export const INCREMENT = 'increment';
export const DECREMENT = 'decrement';// demo_reducer.js
import {INCREMENT,DECREMENT} from './constant.js'
switch(key){case INCREMENT:return preState + data;case DECREMENT:return preState - data;default:return 0; }// demo_action.js
import {INCREMENT,DECREMENT} from './constant.js'
export default function createIncrementAction(data){return {type:INCREMENT,data}
}

以上就是完整的redux,有action,store,reducer,组件和constant五个文件内容,其中组件、store、reducer是必须的。

redux异步编程

action一般为一个对象,action也可以为一个函数,当它为对象时,是同步的,当它是函数时,是异步的。异步action应该包含ajax,计时器等异步任务,对象和数组满足不了,所以异步的action要使用函数实现。

// Demo组件
import store from '../../redux/store'
export default class Demo extends Component{componentDisMount(){// 检测redux的状态更新,当前页面的状态随redux中的更新store.subscribe(()=>{this.setState({})})}asyncAdd = ()=>{setTimeout(()=>{store.dispatch(createIncrementAction(2))},500) // 等待500毫秒后再执行加2,等待动作在组件里,组件等待500毫秒后发送请求去让redux执行一系列操作}render(){return (<div><h1>当前值:{store.getState()}</h1><button onClick={this.asyncAdd}>异步加2</button></div>)}
}// demo_action.js
import {INCREMENT,DECREMENT} from './constant.js'
export default function createIncrementAction(data){return {type:INCREMENT,data}
}

以下尝试实现一个函数类型的action,理想store接收到函数类型的action,丢给reducer处理,等待500毫秒后再通过store.dispatch去生成一个对象类型的action,然后reducer执行+2操作。实际会报错,action传递到store处理必须要是对象类型的。

// 修改组件内异步加2的方法asyncAdd = ()=>{store.dispatch(createAsyncIncrementAction(2,500)); //组件立即发送请求,让redux去等待500毫秒后再执行一系列操作}// 在action文件中增加createAsyncIncrementAction方法,实现异步action
export const createAsyncIncrementAction = (data,time)=>{return ()=>{setTimout(()=>{store.dispatch(createIncrementAction(data))},time)} // return的是一个函数类型的action,里面包含异步任务。store接收到函数类型的action,丢给reducer处理不了函数类型的,所以返回报错
}

引入中间件,当返回的action是一个函数包含异步任务时,由中间件处理异步部分,将异步任务完成后生成的对象类型的action传递给store,再去真正操作数据,解决报错。

npm add redux-thunk
// store.js
// 引入创建store的方法
import {createStore,applyMiddleware} from 'redux'
// 引入为组件服务的reducer
import demoReducer from './demo_reducer'
// 引入redux-thunk
import thunk from 'redux-thunk'
// 创建并暴露store
const store = createStore(demoReducer,applyMiddleware(thunk))
export default store// 在action文件中增加createAsyncIncrementAction方法,实现异步action
export const createAsyncIncrementAction = (data,time)=>{return ()=>{setTimout((dispatch)=>{dispatch(createIncrementAction(data))},time)} // return的是一个函数类型的action,里面包含异步任务。store接收到函数类型的action,传递给中间件处理,此时在处理的依旧是store,所以可以传递dispatch函数,免去store.dispatch调用
}

react-redux

react-redux用来连接redux和react组件。
所有的UI组件都应该包含一个在一个容器组件里,是父子关系。
容器组件才能使用redux的API与redux打交道,UI组件不能使用任何redux的API。
容器组件会通过props传给UI组件redux中保存的状态和用于操作状态的方法。
react-redux模型图
UI组件内不能含有redux,创建容器组件,在容器组件内引入react-redux

npm add react-redux

根据模型图,容器组件要向外连接redux,向内连接UI组件。容器组件通过react-redux连接UI组件,通过store连接redux。

// 容器组件DemoContainer/index.jsx
// 1.引入UI组件
import DemoUI from '../../components/Demo'
// 2.引入react-redux, connect用于连接UI组件和redux
import {connect} from 'react-redux'
// 3.连接UI组件和容器组件
const DemoContainer = connect()(DemoUI); // connect是一个函数,返回值依然是一个函数
export default DemoContainer; // 向外暴露一个容器组件// 在App内
// 1.引入容器组件,不引入UI组件
import DemoContainer from './containers/DemoContainer'
// 2.引入store
import store from '../../redux/store'
export default class App extends Component{render(){return (<div><DemoContainer store={store}/></div>) // 3.连接容器组件和redux,给容器组件传递store}
}

容器组件向UI组件通过props传值,通过connect函数的第一个参数,connect第一个参数为函数,返回的值作为UI组件的状态。connect第二个参数为函数,返回值作为UI组件操作状态的方法。

// mapStateToProps用于传递状态,返回一个对象,key和value对应props的key value
function mapStateToProps(state){// return {n:900}; // 相当于<DemoUI n={900} />return {n:state}
}
// mapDispatchToProps用于传递操作状态的方法,返回一个对象,key和value对应props的key value
function mapDispatchToProps(dispatch){return {add: (data)=>{dispatch(createIncrementAction(data)) // 通知redux做加法}} 
}
export default connect(mapStateToProps,mapDispatchToProps)(DemoUI) 

容器组件的store是靠props传进去的,而不是引入的。

mapDispatchToProps简化

// ui组件
increment = ()=>{const value = this.selectNumber;this.props.jia(value)
}// 容器组件mapDispatchToProps
const mapDispatchToProps = dispatch=>({jia:number => dispatch(createIncrementAction(number)),jian: number => dispatch(createDecrementAction(number))
})
// 继续简化,将mapStateToProps和mapDispatchToProps省略,直接使用connect函数
export default connect(state => ({n:state}), //mapStateToProps简写{jia:createIncrementAction,jian:createDecrementAction} // mapDispatchToProps简写
)(DemoUI) 

容器组件使用connect连接ui组件和redux,容器组件可以自调用subscribe更新页面,所以可以省略store.subscribe的调用。
可以在App index.js内给所有子组件传递store,在子组件内可以不引入store直接使用

// App index.js
import store from './redux/store'
import {Provider} from 'reatc-redux'
ReactDOM.render(<Provider store={store}><App/></Provider>,document.getElementById('root')
)

UI组件可以写在容器组件的文件内,合并为一个文件,只暴露容器组件。
总结,组件与redux打交道:

  1. 定义UI组件,不暴露
  2. 使用connect生成容器组件,并暴露
    connect(
    state => ({key:value}), // 映射状态
    {key:xxxAction} // 映射操作状态的方法
    )(UI组件)
  3. 在UI组件内通过this.props.xxx读取和操作状态
// UI组件和容器组件合并
class Demo extends Component{add = ()=>{this.props.jiafa(1)}render(){return (<div><h1>和为:{this.props.sum}</h1><button onClick={this.add}>点击+1</button></div>)}
}export default connect(state => ({sum:state}), {jiafa:createIncrementAction} 
)(Demo)

组件间数据共享

创建Person组件,创建对应的action, reducer,在store内使用combineReducers把所有组件的reducer合并。

// store.js
import demoRudecer from './reducers/demo'
import personReducer from './reducers/person'
import {combineReducers} from 'redux'
const allReducer = combineReducers({sum:demoReducer,persons:personReducer
}) // combineReducers里传递的对象就是store管理的状态对象// Demo组件里connect使用
state => ({s: state.sum, num: state.persons.length}) // 传递给UI组件使用

纯函数

纯函数只要是同样的输入(实参),必定得到同样的输出(返回)。
纯函数内不能改写参数数据,不能产生副作用(比如不能发送网络请求、不能连接输入输出设备等有可能造成数据丢失的行为),不能调用Date.now或Math.random等返回值不定的方法。
redux的reducer函数必须是一个纯函数。

redux调试工具

引入工具库,可以在浏览器看到redux内的状态

npm add redux-devtools-extension
// store.js
// 引入工具库
import {composeWithDevTools} from 'redux-devtools-extention'
export default createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))

项目打包

执行命令生成build文件夹

npm run build

将生成的build文件夹放到服务器,使用Node或Java搭建服务器,或者使用serve库。

npm i serve -g // 全局安装库
serve a  // 以a文件夹为根目录作为服务器运行
serve // 以当前文件为服务器运行

http://www.ppmy.cn/ops/134801.html

相关文章

Kubernetes 集群中防火墙配置的挑战及替代防护策略

文章目录 Kubernetes 集群中防火墙配置的挑战及替代防护策略Kubernetes 集群中防火墙配置的困难与不稳定性动态网络环境带来的管理复杂性防火墙规则与服务稳定性的潜在冲突集群性能的潜在影响 网络防护的有效方案&#xff1a;在网络边缘进行安全防护集中管理和覆盖更广性能更加…

生成式GPT商品推荐:精准满足用户需求

生成式GPT商品推荐&#xff1a;精准满足用户需求 随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;电商平台正在逐步迎来一场前所未有的变革。尤其是生成式GPT&#xff08;Generative Pre-trained Transformer&#xff09;技术的应用&#xff0c;正在重新定…

微信小程序自定义顶部导航栏(适配各种机型)

效果图 1.pages.js&#xff0c;需要自定义导航栏的页面设置"navigationStyle": "custom" 2.App.vue,获取设备高度及胶囊位置 onLaunch: function () {// 系统信息const systemInfo uni.getSystemInfoSync()// 胶囊按钮位置信息const menuButtonInfo uni.…

3步实现贪吃蛇

方法很简单&#xff0c;打开页面&#xff0c;复制&#xff0c;粘贴 一.整体思维架构 我们根据游戏的开始&#xff0c;运行&#xff0c;结束&#xff0c;将整个游戏划分成三个部分。在每个部分下面又划分出多个功能&#xff0c;接下来我们就根据模块一一实现功能。 二.Gamesta…

38配置管理工具(如Ansible、Puppet、Chef)

每天五分钟学Linux | 第三十八课&#xff1a;配置管理工具&#xff08;如Ansible、Puppet、Chef&#xff09; 大家好&#xff01;欢迎再次来到我们的“每天五分钟学Linux”系列教程。在前面的课程中&#xff0c;我们学习了如何安装和配置邮件服务器。今天&#xff0c;我们将探…

C语言项⽬实践-贪吃蛇

目录 1.项目要点 2.窗口设置 2.1mode命令 2.2title命令 2.3system函数 2.Win32 API 2.1 COORD 2.2 GetStdHandle 2.3 CONSOLE_CURSOR_INFO 2.4 GetConsoleCursorInfo 2.5 SetConsoleCursorInfo 2.5 SetConsoleCursorPosition 2.7 GetAsyncKeyState 3.贪吃蛇游戏设…

【jvm】如何判断一个对象是否可以回收

目录 1.引用计数法1.1 原理1.2 缺点 2.可达性分析算法2.1 原理2.2 GC Roots2.3 标记-清除阶段 3.引用类型 1.引用计数法 1.1 原理 1.为每个对象创建一个引用计数&#xff0c;当有对象引用该对象时&#xff0c;计数器加1。 2.当引用失效时&#xff0c;计数器减1。 3.当计数器的…

1. kafka分布式环境搭建

一. 集群规划 主机名IP组件hadoop1192.168.205.154zookeeper, kafkahadoop2192.168.205.155kafkahadoop3192.168.205.156kafka kafka版本&#xff1a;3.6.0二. 集群部署 安装JDK&#xff0c;具体安装过程此处不赘述。 安装zookeeper&#xff0c;本次采用单机模式部署在hadoo…