react + redux 之 美团案例

news2025/1/21 6:34:31

1.案例展示

![](https://img-blog.csdnimg.cn/direct/b7a9604e5d274504ad630427a996aa8b.png
在这里插入图片描述

2.环境搭建

  1. 克隆项目到本地(内置了基础静态组件和模版)
git clone http://git.itcast.cn/heimaqianduan/redux-meituan.git 
  1. 安装所有依赖
npm i 
  1. 启动mock服务(内置了json-server)
npm run serve 
  1. 启动前端服务
npm run start 

3.分类和商品列表渲染

在这里插入图片描述
1.store modules 下 takeaway.js文件

// 编写store
import { createSlice } from "@reduxjs/toolkit"
import axios from "axios"

const foodsStore = createSlice({
  name: 'foods',
  initialState: {
    // 商品列表
    foodsList: [],a
  },
  reducers: {
    // 更改商品列表
    setFoodsList (state, action) {
      state.foodsList = action.payload
    }
  }
})

// 异步获取部分
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 { fetchFoodsList }

const reducer = foodsStore.reducer
export default reducer

2.store下index.js文件

import foodsReducer from './modules/takeaway'
import { configureStore } from '@reduxjs/toolkit'
const store = configureStore({
  reducer: {
    foods: foodsReducer
  }
})
export default store

3.app.js

import { useDispatch, useSelector } from 'react-redux'
import { fetchFoodsList } from './store/modules/takeaway'
import { useEffect } from 'react'
// 触发action执行
  // 1. useDispatch -> dispatch 2. actionCreater导入进来 3.useEffect
  const dispatch = useDispatch()
  useEffect(() => {
    dispatch(fetchFoodsList())
  }, [dispatch])
  // 获取foodsList渲染数据列表
  // 1. useSelector
  const { foodsList } = useSelector(state => state.foods)
   {/* 外卖商品列表 */}
              {foodsList.map((item, index) => {
                return (
                 <FoodsCategory
                    key={item.tag}
                    // 列表标题
                    name={item.name}
                    // 列表商品
                    foods={item.foods}
                  />
                )
              })}

4.menu.js

import { useDispatch,useSelector } from 'react-redux'
const dispatch = useDispatch()
const {foodsList} = useSelector(state=>state.foods) 

5.index.js

// 注入store
import { Provider } from 'react-redux'
import store from './store'
const root = createRoot(document.getElementById('root'))
root.render(
  <Provider store={store}>
    <App />
  </Provider>
)

4.点击分类激活实现

在这里插入图片描述
1.store modules下 takeaway.js文件


// 编写store
import { createSlice } from "@reduxjs/toolkit"
import axios from "axios"
const foodsStore = createSlice({
  name: 'foods',
  initialState: {
    // 商品列表
    foodsList: [],
    //激活index
    activeIndex:0,
  },
  reducers: {
    // 更改商品列表
    setFoodsList (state, action) {
      state.foodsList = action.payload
    },
    //更改activeIndex
    changeActiveIndex(state,action){
        state.activeIndex = action.payload
    }
  }
})

// 异步获取部分
const { setFoodsList,changeActiveIndex} = foodsStore.actions

2.menu.js

import classNames from 'classnames'
import './index.scss'

import { useDispatch,useSelector } from 'react-redux'
import { changeActiveIndex} from '../../store/modules/takeaway'
const Menu = () => {
  const dispatch = useDispatch()
  const {foodsList,activeIndex} = useSelector(state=>state.foods) 
  const menus = foodsList.map(item => ({ tag: item.tag, name: item.name }))
  return (
    <nav className="list-menu">
      {/* 添加active类名会变成激活状态 */}
      {menus.map((item, index) => {
        return (
          <div
          onClick={() => dispatch(changeActiveIndex(index))}
            key={item.tag}
            className={classNames(
              'list-menu-item',
              activeIndex === index && 'active'
            )}
          >
            {item.name}
          </div>
        )
      })}
    </nav>
  )
}

export default Menu

3.app.js

const { foodsList , activeIndex} = useSelector(state => state.foods)
<div className="goods-list">
              {/* 外卖商品列表 */}
              {foodsList.map((item, index) => {
                return (
                  activeIndex==index && <FoodsCategory
                    key={item.tag}
                    // 列表标题
                    name={item.name}
                    // 列表商品
                    foods={item.foods}
                  />
                )
              })}
            </div>

5.添加购物车

在这里插入图片描述
1.takeaway.js

// 编写store

import { createSlice } from "@reduxjs/toolkit"
import axios from "axios"

const foodsStore = createSlice({
  name: 'foods',
  initialState: {
    // 商品列表
    foodsList: [],
    // 菜单激活下标值
    activeIndex: 0,
    // 购物车列表
    cartList: []
  },
  reducers: {
    // 更改商品列表
    setFoodsList (state, action) {
      state.foodsList = action.payload
    },
    // 更改activeIndex
    changeActiveIndex (state, action) {
      state.activeIndex = action.payload
    },
    // 添加购物车
    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)
      }
    },
  }
})

const { setFoodsList, changeActiveIndex, addCart} = foodsStore.actions
export { fetchFoodsList, changeActiveIndex, addCart}

2.foodItem下index.js文件

import { useDispatch } from 'react-redux'
import { setCarlist } from '../../../store/modules/takeaway'
const dispatch = useDispatch()
 <div className="goods-count">
            <span className="plus" onClick={() => dispatch(setCarlist({
              id,
              picture,
              name,
              unit,
              description,
              food_tag_list,
              month_saled,
              like_ratio_desc,
              price,
              tag,
              count
            }))}></span>
          </div>

6.统计区域功能实现

在这里插入图片描述
在这里插入图片描述
1.cart下面index.js

import classNames from 'classnames'
import { useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import Count from '../Count'
import './index.scss'


const Cart = () => {
  const { carList } = useSelector(state => state.foods)
  // 计算总价 
  const totalPrice = carList.reduce((a, c) => a + c.price * c.count, 0)
  return (
    <div className="cartContainer">
      <div className="cart">
        {/* fill 添加fill类名购物车高亮*/}
        {/* 购物车数量 */}
        <div  className={classNames('icon', carList.length > 0 && 'fill')}>
          {carList.length > 0 && <div className="cartCornerMark">{carList.length}</div>}
        </div>
        {/* 购物车价格 */}
        <div className="main">
          <div className="price">
            <span className="payableAmount">
              <span className="payableAmountUnit">¥</span>
              {totalPrice.toFixed(2)}
            </span>
          </div>
          <span className="text">预估另需配送费 ¥5</span>
        </div>
        {/* 结算 or 起送 */}
        {carList.length > 0 ? (
          <div className="goToPreview">去结算</div>
        ) : (
          <div className="minFee">1元起送</div>
        )}
      </div>
      {/* 添加visible类名 div会显示出来 */}
      <div className={classNames('cartPanel')}>
        <div className="header">
          <span className="text">购物车</span>
          <span className="clearCart">
            清空购物车
          </span>
        </div>

        {/* 购物车列表 */}
        <div className="scrollArea">
          {carList.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">
                  {/* 数量组件 */}
                  <Count
                    count={item.count}
                 
                  />
                </div>
              </div>
            )
          })}
        </div>
      </div>
    </div>
  )
}

export default Cart


7.购物车列表功能实现

在这里插入图片描述
在这里插入图片描述
1.takeaway.js

// 编写store

import { createSlice } from "@reduxjs/toolkit"
import axios from "axios"

const foodsStore = createSlice({
  name: 'foods',
  initialState: {
    // 商品列表
    foodsList: [],
    //激活index
    activeIndex:0,
    //汽车
    carList:[]
  },
  reducers: {
    // 更改商品列表
    setFoodsList (state, action) {
      state.foodsList = action.payload
    },
    //更改activeIndex
    changeActiveIndex(state,action){
        state.activeIndex = action.payload
    },
    setCarlist(state,action){
      // 是否添加过?以action.payload.id去cartList中匹配 匹配到了 添加过
      const item = state.carList.find(item => item.id === action.payload.id)
      if (item) {
        item.count++
      } else {
        state.carList.push(action.payload)
      }
    },
    increCount(state,action){
      const item = state.carList.find(item => item.id === action.payload.id)
      item.count++
    },
    decreCount(state,action){
        const item = state.carList.find(item => item.id === action.payload.id)
        if(item.count===0){
         return
        }
      item.count--
    },
     // 清除购物车
     clearCart (state) {
        state.carList = []
      }
    
}
})

// 异步获取部分
const { setFoodsList,changeActiveIndex,setCarlist,increCount,decreCount,clearCart} = foodsStore.actions
const fetchFoodsList = () => {
  return async (dispatch) => {
    // 编写异步逻辑
    const res = await axios.get('http://localhost:3004/takeaway')
    // 调用dispatch函数提交action
    dispatch(setFoodsList(res.data))
  }
}

export { fetchFoodsList ,changeActiveIndex,setCarlist,increCount,decreCount,clearCart}

const reducer = foodsStore.reducer

export default reducer

2.cart下index文件

import classNames from 'classnames'
import { useDispatch, useSelector } from 'react-redux'
import Count from '../Count'
import './index.scss'
import {increCount,decreCount,clearCart} from '../../store/modules/takeaway'


const Cart = () => {
  const { carList } = useSelector(state => state.foods)
  // 计算总价 
  const totalPrice = carList.reduce((a, c) => a + c.price * c.count, 0)

  const dispatch = useDispatch()

  return (
    <div className="cartContainer">
      <div className="cart">
        {/* fill 添加fill类名购物车高亮*/}
        {/* 购物车数量 */}
        <div  className={classNames('icon', carList.length > 0 && 'fill')}>
          {carList.length > 0 && <div className="cartCornerMark">{carList.length}</div>}
        </div>
        {/* 购物车价格 */}
        <div className="main">
          <div className="price">
            <span className="payableAmount">
              <span className="payableAmountUnit">¥</span>
              {totalPrice.toFixed(2)}
            </span>
          </div>
          <span className="text">预估另需配送费 ¥5</span>
        </div>
        {/* 结算 or 起送 */}
        {carList.length > 0 ? (
          <div className="goToPreview">去结算</div>
        ) : (
          <div className="minFee">1元起送</div>
        )}
      </div>
      {/* 添加visible类名 div会显示出来 */}
      <div className={classNames('cartPanel',carList.length>0&&'visible')} >
        <div className="header">
          <span className="text">购物车</span>
          <span className="clearCart" onClick={()=>dispatch(clearCart())}>
            清空购物车
          </span>
        </div>

        {/* 购物车列表 */}
        <div className="scrollArea">
          {carList.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">
                  {/* 数量组件 */}
                  <Count
                    count={item.count}
                    onPlus={()=>dispatch(increCount({id:item.id}))}
                    onMinus={()=>dispatch(decreCount({id:item.id}))}
                  />
                </div>
              </div>
            )
          })}
        </div>
      </div>
    </div>
  )
}

export default Cart

8.控制购物车显示和隐藏

在这里插入图片描述

在这里插入图片描述
1.cart文件下index.js文件

import classNames from 'classnames'
import { useDispatch, useSelector} from 'react-redux'
import { useState } from 'react'
import Count from '../Count'
import './index.scss'
import {increCount,decreCount,clearCart} from '../../store/modules/takeaway'


const Cart = () => {
  const { carList } = useSelector(state => state.foods)
  // 计算总价 
  const totalPrice = carList.reduce((a, c) => a + c.price * c.count, 0)
  const [visible,setVisible]= useState(false)
  const dispatch = useDispatch()

  const onShow = () => {
    if (carList.length > 0) {
      setVisible(true)
    }
  }
  return (
    <div className="cartContainer">
       {/* 遮罩层 添加visible类名可以显示出来 */}
       <div
        className={classNames('cartOverlay', visible && 'visible')}
        onClick={() => setVisible(false)}
      />
      <div className="cart">
        {/* fill 添加fill类名购物车高亮*/}
        {/* 购物车数量 */}
        <div onClick={onShow}  className={classNames('icon', carList.length > 0 && 'fill')}>
          {carList.length > 0 && <div className="cartCornerMark">{carList.length}</div>}
        </div>
        {/* 购物车价格 */}
        <div className="main">
          <div className="price">
            <span className="payableAmount">
              <span className="payableAmountUnit">¥</span>
              {totalPrice.toFixed(2)}
            </span>
          </div>
          <span className="text">预估另需配送费 ¥5</span>
        </div>
        {/* 结算 or 起送 */}
        {carList.length > 0 ? (
          <div className="goToPreview">去结算</div>
        ) : (
          <div className="minFee">1元起送</div>
        )}
      </div>
      {/* 添加visible类名 div会显示出来 */}
      <div className={classNames('cartPanel',visible &&'visible')} >
        <div className="header">
          <span className="text">购物车</span>
          <span className="clearCart" onClick={()=>dispatch(clearCart())}>
            清空购物车
          </span>
        </div>

        {/* 购物车列表 */}
        <div className="scrollArea">
          {carList.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">
                  {/* 数量组件 */}
                  <Count
                    count={item.count}
                    onPlus={()=>dispatch(increCount({id:item.id}))}
                    onMinus={()=>dispatch(decreCount({id:item.id}))}
                  />
                </div>
              </div>
            )
          })}
        </div>
      </div>
    </div>
  )
}

export default Cart

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1347800.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【模拟电路】软件Circuit JS

一、模拟电路软件Circuit JS 二、Circuit JS软件配置 三、Circuit JS 软件 常见的快捷键 四、Circuit JS软件基础使用 五、Circuit JS软件使用讲解 欧姆定律电阻的串联和并联电容器的充放电过程电感器和实现理想超导的概念电容阻止电压的突变&#xff0c;电感阻止电流的突变LR…

Linux基础知识学习3

vim编辑器 其分为四种模式 1.普通(命令)模式 2.编辑模式 3.底栏模式 4.可视化模式 vim编辑器被称为编辑器之神&#xff0c;而Emacs更是神之编辑器 普通模式&#xff1a; 1.光标移动 ^ 移动到行首 w 跳到下一个单词的开头…

【完整思路】2023 年中国高校大数据挑战赛 赛题 B DNA 存储中的序列聚类与比对

2023 年中国高校大数据挑战赛 赛题 B DNA 存储中的序列聚类与比对 任务 1.错误率和拷贝数分析&#xff1a;分析“train_reads.txt”和“train_reference.txt”数据集中的错误率&#xff08;插入、删除、替换、链断裂&#xff09;和序列拷贝数。 2.聚类模型开发&#xff1a;开发…

【快速全面掌握 WAMPServer】10.HTTP2.0时代,让 WampServer 开启 SSL 吧!

网管小贾 / sysadm.cc 如今的互联网就是个看脸的时代&#xff0c;颜值似乎成了一切&#xff01; 不信&#xff1f;看看那些直播带货的就知道了&#xff0c;颜值与出货量绝对成正比&#xff01; 而相对于 HTTP 来说&#xff0c;HTTPS 绝对算得上是高颜值的帅哥&#xff0c;即安…

java的参数传递机制概述,方法重载概述,以及相关案例

前言&#xff1a; 学了Java的传递机制&#xff0c;稍微记录一下。循循渐进&#xff0c;daydayup&#xff01; java的参数传递机制概述 1&#xff0c;java的参数传递机制是什么&#xff1f; java的参数传递机制是一种值传递机制。 2&#xff0c;值传递是什么&#xff1f; 值…

NGUI基础-三大基础组件之Panel组件

目录 Panel组件 Panel的作用&#xff1a; 注意&#xff1a; 相关关键参数讲解&#xff1a; Alpha&#xff08;透明度值&#xff09;&#xff1a; Depth&#xff08;深度&#xff09;&#xff1a; Clippinng&#xff08;裁剪&#xff09;&#xff1a; ​编辑 None Tex…

八. 实战:CUDA-BEVFusion部署分析-环境搭建

目录 前言0. 简述1. CUDA-BEVFusion浅析2. CUDA-BEVFusion环境配置2.1 简述2.2 源码下载2.3 模型数据下载2.4 基础软件安装2.5 protobuf安装2.5.1 apt 方式安装2.5.2 源码方式安装 2.6 编译运行2.6.1 配置 environment.sh2.6.2 利用TensorRT构建模型2.6.3 编译运行程序 2.7 拓展…

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK设置相机的图像剪切(ROI)功能(C++)

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK设置相机的图像剪切&#xff08;ROI&#xff09;功能&#xff08;C&#xff09; Baumer工业相机Baumer工业相机的图像剪切&#xff08;ROI&#xff09;功能的技术背景CameraExplorer如何使用图像剪切&#xff08;ROI&#xff09;功…

分库分表之Mycat应用学习五

5 Mycat 离线扩缩容 当我们规划了数据分片&#xff0c;而数据已经超过了单个节点的存储上线&#xff0c;或者需要下线节 点的时候&#xff0c;就需要对数据重新分片。 5.1 Mycat 自带的工具 5.1.1 准备工作 1、mycat 所在环境安装 mysql 客户端程序。 2、mycat 的 lib 目录…

汇川PLC(H5U):定时器指令

一、H5U系列的定时器种类 H5U系列PLC的定时器指令都封装成指令块了&#xff0c;共4种类型&#xff1a;脉冲定时器、接通延时定时器、关断延时定时器、时间累加定时器。 H5U系列PLC的定时器时间基准是1ms&#xff0c;在IN引脚的执行指令有效的时候开始跟新计数器的值。 我们知…

以太网转RS485通讯类库封装

最近选用有人科技的以太网转RS485模块做项目&#xff0c;设备真漂亮&#xff0c;国货之光。调通了通讯的代码&#xff0c;发到网上供大家参考&#xff0c;多多交流。 以下分别是配套的头文件与源文件&#xff1a; /*******************************************************…

Git 常用命令知识笔记

Git 仓库数据结构 Git 仓库由一个个的 commit 组成某些 commit 上会有一些 branch 指向它们&#xff0c;这些 branch 的本质是引用有一个特殊的引用叫做 HEAD&#xff0c;它始终指向当前的位置&#xff0c;这个位置可以是 commit&#xff0c;也可以是 branch staging area 暂存…

LTPI协议的理解——1、LTPI协议的定义和结构

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 LTPI协议的理解——1、LTPI协议的定义和结构 定义DC-SCM 2.0 LTPI 结构GPIO通道I2C/SMBus通道Uart通道OEM通道数据通道 总结 定义 LTPI (LVDS Tunneling Protocol & Int…

easyexcel 导出

在使用EasyExcel库进行数据写入时&#xff0c;通常我们会使用实体类来存储数据。但是当遇到动态查询&#xff0c;无法确定属性数量和名称时&#xff0c;就需要使用Map来接收数据。然而&#xff0c;直接将Map中的数据写入Excel表格并不是一件简单的事情。接下来&#xff0c;我将…

计算机操作系统(OS)——P3内存管理

1、内存的基础知识 学习目标&#xff1a; 什么是内存&#xff1f;有何作用&#xff1f; 内存可存放数据。程序执行前__需要先放内存中才能被CPU处理__——缓和CPU与硬盘之间的速度矛盾。 【思考】在多道程序程序下&#xff0c;系统会有多个进程并发执行&#xff0c;也就是说…

PyTorch常用工具(2)预训练模型

文章目录 前言2 预训练模型 前言 在训练神经网络的过程中需要用到很多的工具&#xff0c;最重要的是数据处理、可视化和GPU加速。本章主要介绍PyTorch在这些方面常用的工具模块&#xff0c;合理使用这些工具可以极大地提高编程效率。 由于内容较多&#xff0c;本文分成了五篇…

Centos8之更换DNF源

一、DNF包管理器简介 DNF&#xff08;Dandified Yum&#xff09;是一个用于Fedora、CentOS和RHEL等Linux发行版的包管理器。它是Yum&#xff08;Yellowdog Updater, Modified&#xff09;的下一代版本&#xff0c;旨在提供更快、更可靠的软件包管理体验。以下是一些DNF包管理器…

【YOLO系列】yolo V1 ,V3,V5,V8 解释

文章目录 yolo V1 模型结构图通道数 的 物理意义是什么&#xff1f;输出 7730 怎么理解&#xff1f;YOLO v1 损失函数LOSS yolo V3yolo V5yolo V8 视频来源&#xff1a;https://www.bilibili.com/video/BV13K411t7Zs/ AI视频小助理 一、YOLO系列的目标检测算法&#xff0c;其中…

文章解读与仿真程序复现思路——中国电机工程学报EI\CSCD\北大核心《兼顾捕碳强度与可再生能源消纳的储能容量配置优化方法》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主的专栏栏目《论文与完整程序》 这个标题涉及到两个主要方面&#xff1a;捕碳强度和可再生能源的消纳&#xff0c;以及与之相关的储能容量配置的优化方法。下面我会逐一解读这两个方面&…

ES6之生成器(Generator)

✨ 专栏介绍 在现代Web开发中&#xff0c;JavaScript已经成为了不可或缺的一部分。它不仅可以为网页增加交互性和动态性&#xff0c;还可以在后端开发中使用Node.js构建高效的服务器端应用程序。作为一种灵活且易学的脚本语言&#xff0c;JavaScript具有广泛的应用场景&#x…