React学习——美团小案例——Day3
美团小案例
1. 案例演示
基本开发思路:使用 RTK(Redux Toolkit)来管理应用状态, 组件负责 数据渲染 和 dispatch action
2. 准备环境
- 克隆项目到本地(内置了基础静态组件和模版)
git clone http://git.itcast.cn/heimaqianduan/redux-meituan.git
- 安装所有依赖
npm i
- 启动mock服务(内置了json-server)
npm run serve
- 启动前端服务
npm run start
3. 分类和商品列表渲染
1- 编写store逻辑: src/store/modules/takeaway.js
// 编写store
import { createSlice } from "@reduxjs/toolkit"
import axios from "axios"
// 1、编写同步部分的store
const foodsStore = createSlice({name: 'foods',// (1).初始化值initialState: {// 商品列表foodsList: []},// (2).修改方法reducers: {// 更改商品列表setFoodsList (state, action) {state.foodsList = action.payload}}
})// 2、解构
const { setFoodsList } = foodsStore.actions
// 异步获取部分
const fetchFoodsList = () => {return async (dispatch) => {// 编写异步逻辑const res = await axios.get('http://localhost:3004/takeaway')// 调用dispatch函数提交actiondispatch(setFoodsList(res.data))}
}// 3、导出
export { fetchFoodsList }const reducer = foodsStore.reducer
export default reducer
2 -组合store: src/store/index.js
import { configureStore } from "@reduxjs/toolkit";
import foodsReducer from "./modules/takeaway";const store = configureStore({reducer: {foods:foodsReducer}
})export default store
3- 组件使用store数据: src/App.js
// 省略部分代码
import { useDispatch, useSelector } from 'react-redux'
import { fetchFoodsList } from './store/modules/takeaway'
import { useEffect } from 'react'const App = () => {// 触发action执行// 1. useDispatch -> dispatch // 2. actionCreater导入进来 // 3.useEffectconst dispatch = useDispatch()useEffect(() => {dispatch(fetchFoodsList())}, [dispatch])return (<div className="home">{/* 导航 */}<NavBar />{/* 内容 */}<div className="content-wrap"><div className="content"><Menu /><div className="list-content"><div className="goods-list">{/* 外卖商品列表 */}{foodsList.map(item => {return (<FoodsCategorykey={item.tag}// 列表标题name={item.name}// 列表商品foods={item.foods}/>)})}</div></div></div></div>{/* 购物车 */}<Cart /></div>)
}export default App
4- Menu组件中使用store数据: src/componmens/Menu/index.js
4. 点击分类激活交互实现
1- 编写store逻辑: src/store/modules/takeaway.js
2- 编写组件逻辑: src/componmens/Menu/index.js
5. 商品列表切换显示
src/App.js
6. 添加购物车实现
实现思路:
- 使用RTX管理新状态cartList
(思路:如果添加过,只更新数量count,没有添加过,直接push进去) - 组件中点击时收集数据提交action添加购物车(src/components/FoodsCategory/FoodItem/index.js)
1- 编写store逻辑: src/store/modules/takeaway.js
// 编写storeimport { createSlice } from "@reduxjs/toolkit"
import axios from "axios"const foodsStore = createSlice({name: 'foods',reducers: {// 添加购物车addCart (state, action) {// 是否添加过?以action.payload.id去cartList中匹配 匹配到了 添加过const item = state.cartList.find(item => item.id === action.payload.id)if (item) {item.count++} else {state.cartList.push(action.payload)}}}
})// 导出actionCreater
const { addCart } = foodsStore.actionsexport { addCart }const reducer = foodsStore.reducerexport default reducer
2- 编写组件逻辑: src/components/FoodsCategory/FoodItem/index.js
<div className="goods-count">{/* 添加商品 */}<span className="plus" onClick={() => dispatch(addCart({id,picture,name,unit,description,food_tag_list,month_saled,like_ratio_desc,price,tag,count:count||1}))}></span>
</div>
7. 统计区域实现
实现思路
- 基于store中的cartList的length渲染数量
- 基于store中的cartList累加price * count
- 购物车cartList的length不为零则高亮
// 计算总价
const totalPrice = cartList.reduce((a, c) => a + c.price * c.count, 0){/* fill 添加fill类名购物车高亮*/}
{/* 购物车数量 */}
<div onClick={onShow} className={classNames('icon', cartList.length > 0 && 'fill')}>{cartList.length > 0 && <div className="cartCornerMark">{cartList.length}</div>}
</div>
8. 购物车列表功能实现
1-控制列表渲染
src/components/Cart/index.js
const Cart = () => {return (<div className="cartContainer">{/* 添加visible类名 div会显示出来 */}<div className={classNames('cartPanel', 'visible')}>{/* 购物车列表 */}<div className="scrollArea">{cartList.map(item => {return (<div className="cartItem" key={item.id}><img className="shopPic" src={item.picture} alt="" /><div className="main"><div className="skuInfo"><div className="name">{item.name}</div></div><div className="payableAmount"><span className="yuan">¥</span><span className="price">{item.price}</span></div></div><div className="skuBtnWrapper btnGroup">{/* 数量组件 */}<Countcount={item.count}/></div></div>)})}</div></div></div>)
}export default Cart
2- 购物车增减逻辑实现
src/store/modules/takeaway.js
// count增
increCount (state, action) {// 关键点:找到当前要修改谁的count idconst item = state.cartList.find(item => item.id === action.payload.id)item.count++
},
// count减
decreCount (state, action) {// 关键点:找到当前要修改谁的count idconst item = state.cartList.find(item => item.id === action.payload.id)if (item.count === 0) {return}item.count--
}
src/store/modules/takeaway.js
<div className="skuBtnWrapper btnGroup">{/* 数量组件 */}<Countcount={item.count}onPlus={() => dispatch(increCount({ id: item.id }))}onMinus={() => dispatch(decreCount({ id: item.id }))}/>
</div>
3-清空购物车实现
src/store/modules/takeaway.js
// 清除购物车
clearCart (state) {state.cartList = []
}
src/components/Cart/index.js
import { clearCart } from "../../store/modules/takeaway";
<div className="header"><span className="text">购物车</span><span className="clearCart" onClick={() => dispatch(clearCart())}>清空购物车</span>
</div>
9. 控制购物车显示和隐藏
src/components/Cart/index.js