当前位置: 首页 > news >正文

React18学习笔记(二) React的状态管理工具--Redux,案例--移动端外卖平台

文章目录

      • 一.Redux的基础用法
        • 1.示例:普通网页中的Redux计步器
        • 2.Redux管理数据的流程
        • 3.配套工具和环境准备
          • 3.1.配套工具
          • 3.2.环境准备
        • 4.示例:React项目中的Redux计步器
          • 思路
          • 步骤
            • step1:创建子模块
            • step2:导入子模块
            • step3:注入store实例
            • step4:React组件内使用store中的数据
            • step5:在组件内修改store中的数据
            • step6:优化---定义代参actionCreater来传参
        • 5.示例:使用Redux管理异步状态操作
          • 5.1.创建子模块
          • 5.2.导入子模块
          • 5.3.注入store实例
          • 5.4.在组件中使用
      • 二.Redux案例:某团外卖
        • 1.开发前准备
        • 2.渲染商品分类和商品列表
          • 2.1.创建子模块takeaway
          • 2.2.引入子模块
          • 2.3.把store注入到组件中
          • 2.4.在根组件App中使用和渲染数据
        • 3.点击分类实现高亮
          • 3.1.RTK管理新状态activeIndex
          • 3.2.组件中:点击触发action更新activeIndex,动态控制激活类名
        • 4.点击分类项,实现商品列表的切换显示

一.Redux的基础用法

Redux之于React,类似于Vuex或Pinia之于Vue
Redux可以独立于框架运行,作用是通过几种管理的方式管理应用的状态

1.示例:普通网页中的Redux计步器

由于Redux可以独立于React框架,因此可在网页中使用
步骤:

-step1:定义一个reducer函数
该函数根据当前想要做的修改返回一个新状态,新状态的作用是根据不同的Action对象,返回不同的新state(原则:状态不可变)
示例:function myReducer(state,action){}-step2:使用createStore方法中的reducer函数生成一个store实例对象
示例:const store=Redux.createStore(myReducer)-step3:使用store.subscribe方法,订阅数据的变化
(数据一旦变化就会得到通知)
示例:storesubscribe(()=>{})-step4:使用store.dispatch方法,提交action对象,触发数据变化
dispatch提交一个action来更新状态
示例:store.dispatch({type:'INCREMENT'})-step5:使用store.getState方法,获取最新的状态并更新到视图中
示例:const state=stor.getState()

完整代码:

<!DOCTYPE html>
<html>
<head><title>Redux计数器示例</title><!-- 引入Redux库 --><script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.2.1/redux.min.js"></script>
</head>
<body><h1>Redux计数器</h1><div><button id="decrement">-</button><span id="counter-value">0</span><button id="increment">+</button></div><script>// 1.定义reducer函数function myReducer(state = { count: 0 }, action) {// 判断type:type的值是从action对象中获取的,而action对象是由dispatch函数生成的。if (action.type === 'INCREMENT') return { count: state.count + 1 };//增加:返回新的状态对象if (action.type === 'DECREMENT') return { count: state.count - 1 };}// 2.使用reducer函数生成store实例const store = Redux.createStore(myReducer);// 3.通过store实例的subscribe订阅数据变化store.subscribe(() => {//该回调会在每次store更新时被调用。// 5.通过store实例的getState方法获取当前状态值,并更新到页面上。const state = store.getState();// 更新页面上的计数器显示document.getElementById('counter-value').textContent = state.count;});// 4.通过store实例的dispatch函数提交action更改状态/*在Redux中修改数据的唯一方式是:通过dispatch提交一个action对象来更新状态*/document.getElementById('increment').addEventListener('click', () => {store.dispatch({ type: 'INCREMENT' });// 提交增加计数器的action对象});document.getElementById('decrement').addEventListener('click', () => {store.dispatch({ type: 'DECREMENT' });// 提交减少计数器的action对象});</script>
</body>
</html>
2.Redux管理数据的流程

在这里插入图片描述
出于职责清晰和数据流向明确的考量,在Redux中,把修改数据的流程分为三个核心概念:

  • state对象:存放管理的数据状态
  • action对象:描述如何修改数据的
  • reducer函数:形参是state和action,根据acton对象的描述生成一个新的state
3.配套工具和环境准备
3.1.配套工具
  • Redux Toolkit插件(RTK)

官方推荐的编写Redux逻辑的方式,是一套工具的集合,能简化书写方式

优点:简化store的配置方式内置immer支持可变式状态修改内置thunk更好的异步创建
  • react-dedux插件

用来链接Redux和React组件的中间件
(Redux向React组件获取状态,React组件向Redux更新状态,都可以通过这个中间件)

3.2.环境准备

创建项目:npx create-react-app my-react-redux(项目名)
进入项目目录:cd my-react-redux
安装配套工具:npm i @reduxjs/toolkit react-redux(插件)
启动项目:npm run start

项目store目录:

src
├─store               用于集中状态管理
│  ├─modules		用于在内部编写业务分类的子store(应用通常有多个子store模块)
│  │  └─index.js      作用是组合modules中的所有子模块并在store中导入 
4.示例:React项目中的Redux计步器
思路
  • Redux store配置:配置counterStore模块,配置根store并导入counterStore模块
  • React组件:注入store(react-redux),使用和修改store中的数据
步骤
step1:创建子模块

使用React ToolkitcreateSlice()方法创建counterStore子模块

//store/modules/counterStore.jsimport {createSlice} from "@reduxjs/toolkit";
const counterstore=createSlice({name:"counter",// 初始化stateinitialState:{//----类似于Vuex中的Storecount:0},// 定义reducer函数reducers:{//----类似于vuex中的mutationsincrement(state){state.count++},decrement(state){state.count--}}
})// 解构出actionCreater函数
const {increment,decrement}=counterstore.actions
// 获取reducer函数
const reducer=counterstore.reducer
// 按需导出actionCreater函数
export {increment,decrement}
// 默认导出reducer函数
export default reducer
step2:导入子模块
//src/store/index.jsimport { configureStore } from "@reduxjs/toolkit";
// 导入子模块的reducer
import counterReducer from "./modules/counterStore";
export const store = configureStore({reducer: {// 注册子模块的reducer函数counter: counterReducer,},
});
step3:注入store实例

react-redux可以链接Redux和React,
其内置的Provider组件可以通过store参数,把创建好的store实例注入到应用中,从而正式建立链接

//src/index.jsimport { Provider } from 'react-redux';
import { store } from './store';
....{/* 注入:将store注入到Provider组件的props中,然后包裹App组件。 */}<Provider store={store}><App /></Provider>
step4:React组件内使用store中的数据

钩子函数useSelector可以把store中的数据映射到组件中

//App.jsimport { useSelector } from "react-redux";
function App() {const { count } = useSelector((state) => state.counter);//来自src/store/index.js的reducer函数return (<div className="App"><h1>Counter: {count}</h1></div>);
}
export default App;
step5:在组件内修改store中的数据

钩子函数useDispatch可以生成dispatch函数,用于提交action对象

//App.js
// 导入actionCreater
import {increment,decrement} from "./store/modules/counterStore";{/* 增加按钮,触发 increment action */}
<button onClick={() => dispatch(increment())}>+</button>
{/* 显示计数器 */}
<h1>Counter: {count}</h1>
{/* 减少按钮,触发 decrement action */}
<button onClick={() => dispatch(decrement())}>-</button>

*子模块counterStore中,在reducers中定义的increment()decrement()被称为actionCreater方法

step6:优化—定义代参actionCreater来传参

App组件新增"addTo10"和"addTo20"两个按钮,
步骤:
* 在reducers的同步修改方法中添加action对象参数
* 在调用actionCreater时传参
* 参数传到action对象的payload属性上
代码:

//counterStore.js
.....// 定义reducer函数reducers:{...addToNum(state,action){state.count+=action.payload}}
// 解构出actionCreater函数
const {increment,decrement,addToNum}=counterstore.actions
// 获取reducer函数
const reducer=counterstore.reducer
// 按需导出actionCreater函数
export {increment,decrement,addToNum}
// 默认导出reducer函数
export default reducer//App.js
import {increment,decrement,addToNum} from "./store/modules/counterStore";<button onClick={() => dispatch(addToNum(10))}>+10</button><button onClick={() => dispatch(addToNum(20))}>+20</button>

总结:

  • useSelector:获取store中的数据
  • useDispatch:获取dispatch方法
  • 得到要提交的action对象:dispatch(increment())//执行store模块中导出的actionCreater方法,即increment()方法
5.示例:使用Redux管理异步状态操作
5.1.创建子模块
//新建store/modules/channelStore.js//step1:创建子模块channelStore.js,代码不变
import { createSlice } from "@reduxjs/toolkit";
import axios from "axios";const channelstore=createSlice({name:'channel',initialState:{channelList:[]},reducers:{setChannel(state,action){state.channelList=action.payload}}
})/**异步请求部分 */
// step2:单独封装一个函数
const fetchChannelList=()=>{// const dispatch=useDispatch()// 在函数内部return一个新函数,// 在新函数中封装异步请求,// 并调用actionCreater方法传入异步数据生成一个action对象并使用dispatch方法提交return async (dispatch)=>{const res=await axios.get("https://geek.itheima.net/v1_0/channels")dispatch(setChannel(res.data.data.channels))console.log("res:",res.data.data.channels)}
}// 解构出actionCreater函数
const {setChannel}=channelstore.actions
// 按需导出actionCreater函数和异步请求函数
export {setChannel,fetchChannelList}// 获取reducer函数
const reducers=channelstore.reducer
// 默认导出reducer函数
export default reducers
5.2.导入子模块
//store/index.js
import { configureStore } from "@reduxjs/toolkit";
// 导入子模块的reducer
import counterReducer from "./modules/counterStore";
import channelReducer from "./modules/channelStore";
export const store = configureStore({reducer: {// 注册子模块的reudcer函数counter: counterReducer,channel: channelReducer,},
});
5.3.注入store实例

略,无新增代码

5.4.在组件中使用
//App.js
import { fetchChannelList } from "./store/modules/channelStore";
import { useEffect } from "react";function App(){// 使用 dispatch 来触发 actionconst dispatch = useDispatch();// 使用useEffect来触发异步请求执行useEffect(() => {// 触发 fetchChannelList actiondispatch(fetchChannelList());}, [dispatch]);return(<ul>{channelList.map((item) => (<li key={item.id}>{item.name}</li>))}</ul>)
}

二.Redux案例:某团外卖

在这里插入图片描述
功能:

	* 1.商品列表和分类渲染* 2.添加商品* 3.购物车操作* 4.订单数量统计和高亮实现

思路:
     使用Redux Toolkit(RTK)来管理应用状态,
     组件负责数据渲染和dispatch action

1.开发前准备
step1:从远程仓库克隆本地git clone http://git.itcast.cn/heimaqianduan/redux-meituan.git
step2:安装所有依赖npm i
step3:启动mock服务(内置json-server)npm run serve
step4:启动前端服务npm run start
2.渲染商品分类和商品列表
2.1.创建子模块takeaway
//store/modules/takeaway.js
import { createSlice } from "@reduxjs/toolkit";
import axios from "axios";const foodsStore = createSlice({name: "foods",initialState: { foodsList: [] },reducers: {setFoodsList(state, action) {state.foodsList = action.payload;}}
})// 解构createAction方法
const { setFoodsList } = foodsStore.actions// 异步
const fetchFoodsList = () => {return async (dispatch) => {// 获取数据const res = await axios.get("http://localhost:3004/takeaway");// dispatch提交,生成新的action对象dispatch(setFoodsList(res.data))}
}// 按需导出
export { setFoodsList, fetchFoodsList }// 获取reducer
const reducer = foodsStore.reducer
// 默认导出
export default reducer
2.2.引入子模块
//src/store/index.js import { configureStore } from "@reduxjs/toolkit";
// 导入子模块的reducer
import foodsReducer from "./modules/takeaway";export default configureStore({reducer: {// 这里的key要和子模块的name一致foods: foodsReducer,},
});
2.3.把store注入到组件中
//index.jsimport { createRoot } from 'react-dom/client'
import {Provider} from 'react-redux'
import App from './App'
import store from './store'const root = createRoot(document.getElementById('root'))
root.render(// 注入:把store注入到Provider组件中,然后包裹App组件<Provider store={store}><App /></Provider>
)
2.4.在根组件App中使用和渲染数据
//App.js
import { useDispatch,useSelector } from 'react-redux'
import { useEffect } from 'react'
import { fetchFoodsList } from './store/modules/takeaway'//注释掉静态数据const foodsList=[]const App=()=>{/*触发action执行*/const dispatch=useDispatch()// 使用useEffect钩子,在组件加载完成后获取外卖商品列表useEffect(()=>{dispatch(fetchFoodsList())// 派发一个异步获取外卖商品列表的action},[dispatch])/*获取foodsList渲染数据列表*/// 使用useSelector钩子获取foodsList数据const {foodsList} = useSelector(state => state.foods)
}
3.点击分类实现高亮

即点击左边侧边栏中的分类获得高亮选中,右边的对应内容在页面中渲染

高亮和对应的内容如何绑定?

  • 记录当前的点击项activeIndex
  • 动态控制类名,判断条件activeIndex===index

步骤:
    1.RTK管理新状态activeIndex
    2.组件中点击触发action,更改activeIndex
    3.动态控制激活类名显示

3.1.RTK管理新状态activeIndex
//store/modules/takeaway.jsconst foodsStore = createSlice({name: "foods",initialState: {foodsList: [] ,//菜单列表activeIndex: 0 // 当前激活的菜单索引},reducers: {.....// 更改当前激活的菜单索引changeActiveIndex(state, action) {state.activeIndex = action.payload;}}
})
// 解构createAction方法
const { setFoodsList ,changeActiveIndex} = foodsStore.actions
// 按需导出
export { fetchFoodsList, changeActiveIndex }
3.2.组件中:点击触发action更新activeIndex,动态控制激活类名
//src/components/menus/index.jsimport classNames from 'classnames'
import './index.scss'
import { useSelector, useDispatch } from 'react-redux'
import { useEffect } from 'react'
import { changeActiveIndex } from '../../store/modules/takeaway'const Menu = () => {// 从redux中获取数据
const { foodsList,activeIndex} = useSelector((state) => state.foods);
////过滤条件: 过滤出所有标签,并且去除重复的
const menus=foodsList.map(item=>({tag:item.tag,name:item.name}))
const dispatch = useDispatch()// 使用useEffect触发actionuseEffect(() => {dispatch(changeActiveIndex(0))//初始值让渲染有默认选中}, [dispatch])return (<nav className="list-menu">{/* 添加active类名会变成激活状态 */}{menus.map((item, index) => {return (<divonClick={() => {dispatch(changeActiveIndex(index))}}key={item.tag}className={classNames('list-menu-item',activeIndex===index&&'active')}>{item.name}</div>)})}</nav>)
}
export default Menu
4.点击分类项,实现商品列表的切换显示

条件渲染,控制对应项的显隐activeIndex===index&&<div></div>

//App.js
const { foodsList, activeIndex } = useSelector(state => state.foods)
....
{/* 外卖商品列表 */}
{foodsList.map((item, index) => {return (activeIndex === index && <FoodsCategorykey={item.tag}// 列表标题name={item.name}// 列表商品foods={item.foods}/>)
})}

文章转载自:

http://QUnAgxpf.mwrxz.cn
http://cIXnGJyO.mwrxz.cn
http://7yA3ZZ5n.mwrxz.cn
http://hJAmzHMG.mwrxz.cn
http://ItZ4HAkD.mwrxz.cn
http://ZvUA0Nv0.mwrxz.cn
http://XI7KW9hh.mwrxz.cn
http://EjxldUJ4.mwrxz.cn
http://dA93vJca.mwrxz.cn
http://yl5a4p5D.mwrxz.cn
http://36kr0LA4.mwrxz.cn
http://ZgaBnnjy.mwrxz.cn
http://2aA1Fb6n.mwrxz.cn
http://43YJ23WP.mwrxz.cn
http://mbydPQM8.mwrxz.cn
http://wLzDQkD9.mwrxz.cn
http://E040aqeo.mwrxz.cn
http://OxHFc2LC.mwrxz.cn
http://aIidGqYG.mwrxz.cn
http://zIEB4R0L.mwrxz.cn
http://QBoTKcOi.mwrxz.cn
http://RDonxInJ.mwrxz.cn
http://SikATNKX.mwrxz.cn
http://1DUOUOAH.mwrxz.cn
http://Mp88uAFD.mwrxz.cn
http://rdWKcjWD.mwrxz.cn
http://MArOkrj0.mwrxz.cn
http://PUGKnJJm.mwrxz.cn
http://jyFfCAHA.mwrxz.cn
http://17i1QiRC.mwrxz.cn
http://www.dtcms.com/a/385905.html

相关文章:

  • ReactJS + DynamoDB 性能优化方案
  • Next.js与React服务端渲染演进全解析
  • C++ `std::future` 与 `std::promise` 超全解析笔记
  • VScode插件Remote-SSH
  • 挣脱网络桎梏:CapsWriter-Offline+cpolar,让高效输入不受网络牵绊
  • Qt地图软件开发/GIS软件开发组件/修改天地图支持21级别/离线瓦片地图
  • Kafka 跨集群地理复制(Geo-Replication)
  • ​​[硬件电路-235]:双极型三极管、MOS场效应管、IGBT管异同比较
  • Spark专题-第二部分:Spark SQL 入门(1)-Spark SQL 简介
  • Spark源码学习分享之submit提交流程(1)
  • 5、二叉树-小堆
  • 技术奇点爆发周:2025 年 9 月科技突破全景扫描
  • 从Dubbo到SpringCloud Alibaba:大型项目迁移的实战手册(含成本分析与踩坑全记录)(一)
  • 【算法】C语言多组输入输出模板
  • 测试 Docker 的实时恢复功能
  • 系统中间件与云虚拟化-serverless-基于阿里云函数计算的云工作流CloudFlow设计与体验
  • springboot netty 客户端网络编程入门与实战
  • TCP/IP模型
  • 智慧用电安全管理系统的核心优势
  • flutter结合NestedScrollView+TabBar实现嵌套滚动
  • 基于定制开发开源AI智能名片S2B2C商城小程序的社群团购线上平台搭建研究
  • DEDECMS 小程序插件简介 2.0全新上线
  • 详解 Spring Boot 单元测试:@SpringBootTest 与 JUnit 依赖配置及环境注入
  • JMeter元件简介与JMeter测试计划
  • 陪诊小程序:让医疗关怀触手可及
  • n*n矩阵方程组Ax=b,使用Eigen矩阵库常用解法介绍
  • IvorySQL 4.6:DocumentDB+FerretDB 实现 MongoDB 兼容部署指南
  • UART,IIC,SPI总线(通信协议)
  • 记录一次小程序请求报错:600001
  • 光谱相机的新兴领域应用