react 之 美团案例

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【React源码 - ReactDom.render发生了什么】

在React开发中&#xff0c;在入口文件我们都会执行ReactDom.render来讲整个应用挂载在主document中&#xff0c;那其中发生了什么&#xff0c;React是如何讲我们写的JSX代码&#xff0c;一步一步更新Fiber进而挂载渲染的呢。本文主要是基于react17.0.2的源码以及自己的理解来简…

linux 内核模块

linux 内核模块 1. 内核相关命令与文件内核模块存放位置查看已加载内核模块加载与卸载内核模块修改内核参数永久调整内核参数 2. 常用模块进程调度模块进程间通信模块内存管理模块文件系统模块网络接口模块 Linux 内核采用的是模块化技术&#xff0c;这样的设计使得系统内核可以…

Jmeter吞吐量控制器总结

吞吐量控制器(Throughput Controller) 场景&#xff1a; 在同一个线程组里, 有10个并发, 7个做A业务, 3个做B业务,要模拟这种场景,可以通过吞吐量模拟器来实现。 添加吞吐量控制器 用法1: Percent Executions 在一个线程组内分别建立两个吞吐量控制器, 分别放业务A和业务B …

Docker本地部署开源浏览器Firefox并远程访问进行测试

文章目录 1. 部署Firefox2. 本地访问Firefox3. Linux安装Cpolar4. 配置Firefox公网地址5. 远程访问Firefox6. 固定Firefox公网地址7. 固定地址访问Firefox Firefox是一款免费开源的网页浏览器&#xff0c;由Mozilla基金会开发和维护。它是第一个成功挑战微软Internet Explorer浏…

KaiwuDB 受邀亮相“ACDU 中国行”数据库技术沙龙(西安站)

12月23日&#xff0c;由中国计算机学会&#xff08;CCF&#xff09;指导&#xff0c;浪潮数据库、中国数据库联盟、墨天轮数据库社区等联合主办的“ACDU 中国行”数据库技术发展与实践沙龙&#xff08;西安站&#xff09;成功举办。KaiwuDB 高级架构师赵衎衎受邀出席沙龙并做主…

C++ Qt开发:SqlRelationalTable关联表组件

Qt 是一个跨平台C图形界面开发库&#xff0c;利用Qt可以快速开发跨平台窗体应用程序&#xff0c;在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置&#xff0c;实现图形化开发极大的方便了开发效率&#xff0c;本章将重点介绍SqlRelationalTable关联表组件的常用方法及灵…

【C语言】程序练习(三)

大家好&#xff0c;这里是争做图书馆扫地僧的小白。非常感谢各位的支持&#xff0c;也期待着您的关注。 目前博主有着C语言、C、linux以及数据结构的专栏&#xff0c;内容正在逐步的更新。 希望对各位朋友有所帮助同时也期望可以得到各位的支持&#xff0c;有任何问题欢迎私信与…

【华为数据之道学习笔记】7-3基于物理世界的“硬感知”能力

“硬感知”能力的分类 数据采集方式主要经历了人工采集和自动采集两个阶段。自动采集技术仍在发展中&#xff0c;不同的应用领域所使用的具体技术手段也不同。基于物理世界的“硬感知”依靠的就是数据采集&#xff0c;是将物理对象镜像到数字世界中的主要通道&#xff0c;是构建…

进阶版求字符串长度

大家好呀&#xff01;&#xff01;&#xff01;我是Beilef&#xff0c;一个努力的跨界者&#xff0c;今天带来的每日一题是用简单的函数还有递归求字符串长度。下面开始啦&#xff0c;不对的地方请留言。感谢您的斧正。 文章目录 目录 文章目录 前言 一、题目展示 解题思路&am…

Mybatis行为配置之Ⅳ—日志

专栏精选 引入Mybatis Mybatis的快速入门 Mybatis的增删改查扩展功能说明 mapper映射的参数和结果 Mybatis复杂类型的结果映射 Mybatis基于注解的结果映射 Mybatis枚举类型处理和类型处理器 再谈动态SQL Mybatis配置入门 Mybatis行为配置之Ⅰ—缓存 Mybatis行为配置…

HCIA-Datacom题库(自己整理分类的)——ARP协议【完】

一、单选 1.ARP 属于哪一层协议&#xff1f; 数据链路层 网络层 物理层 传输层 2.ARP请求是____发送的 点播 广播 组播 单播 关于ARP报文的说法错误的是? ARP请求报文是广播发送的 ARP报文不能被转发到其他广播域 ARP应答报文是单播方发送的 任何链路层协议都需…

java spring boot 自定义 aop

以一个锁的加锁和释放为例 1、先定义注解 /*** 锁切面* author fmj*/ Retention(RetentionPolicy.RUNTIME) Target(ElementType.METHOD) public interface VersionLockAOP { }2、然后定义切面类以及切点 /*** 切面*/ Component Aspect Slf4j public class VersionLockAOPAspe…

Jackson—Anti-Human IgE Antibodies

Jackson lmmunoResearch推出一系列适用于诊断试剂研发的Mouse Monoclonal Anti-Human IgE antibodies&#xff08;小鼠抗人IgE单克隆抗体&#xff09;&#xff0c;补充了Jackson现有的抗人lgG、IgM和lgA抗体产品&#xff0c;抗人IgE可与一系列特定的报告分子偶联&#xff08;如…

融汇贯通 —— 2023年技术与心灵的双重成长旅程

当我们站在2023年的岁末&#xff0c;回望这一年赋予我们的经历和挑战&#xff0c;心中涌动的感慨与启示像朝日初升的光芒&#xff0c;照亮脚下的路&#xff0c;亦照见心中的路。在此&#xff0c;我想分享几个方面的感悟和成长&#xff0c;愿它们能有所触动&#xff0c;成为您前…

上升到人生法则的贝叶斯理论

贝叶斯定理在数据分析、机器学习和人工智能等领域有广泛的应用。贝叶斯定理&#xff08;Bayes’ theorem&#xff09;是一种用于计算条件概率的重要定理&#xff0c;它基于条件概率的定义&#xff0c;描述了在已知某一条件下&#xff0c;另一个条件发生的概率。 需要注意的是&a…

STM32 CubeMX工具在TSL2561驱动开发中的快速集成与调试技巧

在STM32 CubeMX工具中快速集成和调试TSL2561驱动的开发技巧&#xff0c;可以大大提高开发效率和减少调试时间。下面将为您介绍如何在CubeMX中进行快速集成和调试TSL2561驱动的技巧和步骤。 1. 创建新工程和选择芯片型号 打开STM32 CubeMX工具&#xff0c;点击“New Project”…

课件如何录屏解说?解说技巧,快来围观!

随着在线教育和知识共享理念的普及&#xff0c;越来越多的老师和学生开始尝试录制课件来进行知识传播和课程讲解。课件录屏是将课件内容、讲解声音和画面融合在一起的一种方式&#xff0c;有助于观众更好地理解和掌握知识。可是课件如何录屏解说呢&#xff1f;本文将介绍两种常…

设计模式-对象池模式

设计模式专栏 模式介绍模式特点应用场景对象池模式和工厂模式的区别代码示例Java实现对象池模式Python实现对象池模式 对象池模式在spring中的应用 模式介绍 对象池模式是一种创建型设计模式&#xff0c;它将对象预先创建并初始化后放入一个池中&#xff0c;以供其他对象使用。…

关于Unity使用图片字体示例

1.使用TexturePacker打包图集 下载地址 TexturePacker - Create Sprite Sheets for your game! 2.准备好数字图 3. 导入图片 4. 打包图集需要的设置 将重心点设置为左下方 点击回车 > 后点击回 >到精灵列表 选择导出的格式 导出后的内容 >导入unity 导入 >…

Linux Debian12安装和使用ImageMagick图像处理工具 常见图片png、jpg格式转webp格式

一、ImageMagick简介 ImageMagick是一套功能强大、稳定而且免费的工具集和开发包。可以用来读、写和图像格式转换&#xff0c;可以处理超过100种图像格式&#xff0c;包括流行的TIFF, JPEG, GIF, PNG, PDF以及PhotoCD等格式。对图片的操作&#xff0c;即可以通过命令行进行&am…