React 函数式组件封装购物车(本文章对入门选手不是很友好)

分析说明

  • 注意:React 脚手架默认支持 sass,但是需要自己手动安装 sass 依赖包(用来解析 sass 语法)安装命令:yarn/npm add sass

步骤

  1. 根据模板搭建基本页面结构

  2. 安装解析 sass 的包:yarn add sass,重启项目

  3. 安装 bootstrap:yarn add bootstrap@4.5.0,并导入 bootstrap 样式文件

  4. 安装 axios : npm add axios

实现功能:

        全选、单选、总价计算、数量增减、总数

模拟数据data.json:

{
  "goodsList": [
    {
      "id": 1,
      "goods_name": "班俏BANQIAO超火ins潮卫衣女士2020秋季新款韩版宽松慵懒风薄款外套带帽上衣",
      "goods_img": "https://www.escook.cn/vuebase/pics/1.png",
      "goods_price": 108,
      "goods_count": 1,
      "goods_state": true
    },
    {
      "id": 2,
      "goods_name": "嘉叶希连帽卫衣女春秋薄款2020新款宽松bf韩版字母印花中长款外套ins潮",
      "goods_img": "https://www.escook.cn/vuebase/pics/2.png",
      "goods_price": 129,
      "goods_count": 1,
      "goods_state": true
    },
    {
      "id": 3,
      "goods_name": "思蜜怡2020休闲运动套装女春秋季新款时尚大码宽松长袖卫衣两件套",
      "goods_img": "https://www.escook.cn/vuebase/pics/3.png",
      "goods_price": 198,
      "goods_count": 1,
      "goods_state": false
    },
    {
      "id": 4,
      "goods_name": "思蜜怡卫衣女加绒加厚2020秋冬装新款韩版宽松上衣连帽中长款外套",
      "goods_img": "https://www.escook.cn/vuebase/pics/4.png",
      "goods_price": 99,
      "goods_count": 1,
      "goods_state": false
    },
    {
      "id": 5,
      "goods_name": "幂凝早秋季卫衣女春秋装韩版宽松中长款假两件上衣薄款ins盐系外套潮",
      "goods_img": "https://www.escook.cn/vuebase/pics/5.png",
      "goods_price": 156,
      "goods_count": 1,
      "goods_state": true
    },
    {
      "id": 6,
      "goods_name": "ME&CITY女装冬季新款针织抽绳休闲连帽卫衣女",
      "goods_img": "https://www.escook.cn/vuebase/pics/6.png",
      "goods_price": 142.8,
      "goods_count": 1,
      "goods_state": true
    },
    {
      "id": 7,
      "goods_name": "幂凝假两件女士卫衣秋冬女装2020年新款韩版宽松春秋季薄款ins潮外套",
      "goods_img": "https://www.escook.cn/vuebase/pics/7.png",
      "goods_price": 219,
      "goods_count": 2,
      "goods_state": true
    },
    {
      "id": 8,
      "goods_name": "依魅人2020休闲运动衣套装女秋季新款秋季韩版宽松卫衣 时尚两件套",
      "goods_img": "https://www.escook.cn/vuebase/pics/8.png",
      "goods_price": 178,
      "goods_count": 1,
      "goods_state": true
    },
    {
      "id": 9,
      "goods_name": "芷臻(zhizhen)加厚卫衣2020春秋季女长袖韩版宽松短款加绒春秋装连帽开衫外套冬",
      "goods_img": "https://www.escook.cn/vuebase/pics/9.png",
      "goods_price": 128,
      "goods_count": 1,
      "goods_state": false
    },
    {
      "id": 10,
      "goods_name": "Semir森马卫衣女冬装2019新款可爱甜美大撞色小清新连帽薄绒女士套头衫",
      "goods_img": "https://www.escook.cn/vuebase/pics/10.png",
      "goods_price": 153,
      "goods_count": 1,
      "goods_state": false
    }
  ]
}

*注意文中接口地址是通过json-server本地模拟的(在安装了npm情况下):

npm i json-server -g

​​​​​​​

npx json-server  data.json --port 8888   //在data.json目录下打开终端

一、index.js 中引入bootstrap:

import 'bootstrap/dist/css/bootstrap.css';

二、在src目录下创建文件夹Component,在component目录下分别建立三个文件夹(Carheader、CarFooter、GoodsItem)并在这三个目录下分别建立index.jsx,index.scss

 三、在src目录下找到App.css文件加入一下代码(完成布局)

.app {
  padding-top: 45px;
  padding-bottom: 50px;
}

四、ctrl C/ctrl V

App.js

import logo from './logo.svg';
import './App.css';
import axios from 'axios';

import { CarHeader } from './components/Carwu/CarHeader'; //这里是自己下对应的文件夹下index.jsx文件
import { CarFooter } from './components/Carwu/CarFooter';
import { GoodsItem } from './components/Carwu/GoodsItem';

import { useState,useEffect } from 'react';
function App() {
  const [list,setlist]=useState([])
  const [checkAll,setcheckAll]=useState(false)
  const getlist=async ()=>{
    const {data}=await axios.get('http://localhost:8888/goodsList')
    // console.log(data);
    setlist(data)
    setcheckAll(!data.some((item)=>!item.goods_state))
  }
  //全选
  const updtasetcheckAll=async (state)=>{
    setcheckAll(state)
    //本地修改
    setlist(
      list.map(item=>
        {
        return {
          ...item,
          goods_state:state
        }
      })
    )
    //接口修改
    await Promise.all(list.map(item=>{
      axios.patch(`http://localhost:8888/goodsList/${item.id}`,{goods_state:state})
    }))
  }
  const getState=async (id,goods_state)=>{
    // console.log(id,state);
    list.find(item=>item.id==id).goods_state=goods_state
    setlist([...list])
    setcheckAll(!list.some((item)=>!item.goods_state))


    await axios.patch(`http://localhost:8888/goodsList/${id}`,{goods_state})
  }
  useEffect(()=>{
    getlist()
  },[])
  //数量增加
  const addNum=(id,count)=>{
    //处理本地
    list.find(item=>item.id==id).goods_count=count+1
    setlist([...list])
    // 接口同步
    const newcount=list.find(item=>item.id==id).goods_count
    axios.patch(`http://localhost:8888/goodsList/${id}`,{goods_count:newcount})

  }
  //数量减少
  const reduceNum=async (id,count)=>{
    // console.log(id,count);
    if(count>1){
      //处理本地
      list.find(item=>item.id==id).goods_count=count-1
      setlist([...list])
      // 接口同步
      const newcount=list.find(item=>item.id==id).goods_count
      await axios.patch(`http://localhost:8888/goodsList/${id}`,{goods_count:newcount})
    }
    else if(count<=1){
      if(window.confirm("是否想要删除")){
        await axios.delete(`http://localhost:8888/goodsList/${id}`)
        getlist()
      }
    }
  }
  //计算总数量
  const totalCount=list.reduce((count,item)=>{
    if(item.goods_state){
      return count+ item.goods_count
    }
    return count
  },0)
  //计算总价格
  const totalPrice=list.reduce((price,item)=>{
    if(item.goods_state){
      return price+ item.goods_count * item.goods_price
    }
    return price
  },0)

  return (
    <div className="app">

      <CarHeader>购物车</CarHeader>
      
      {
        list.map(item=><GoodsItem key={item.id} {...item} getState={getState} addNum={addNum} reduceNum={reduceNum}></GoodsItem>)
      }
      <CarFooter checkAll={checkAll} setcheckAll={updtasetcheckAll} totalCount={totalCount} totalPrice={totalPrice}></CarFooter>
    </div>
  );
}

export default App;

CarHeader下的index.jsx文件:

import './index.css'

export const CarHeader=(props)=>{
    return (
        <div className="cart-header">{props.children}</div>
    )
}

CarHeader下的index.scss文件:

.cart-header {
    z-index: 999;
    height: 45px;
    line-height: 45px;
    text-align: center;
    background-color: #1d7bff;
    color: #fff;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
  }

GoodsItem下的index.jsx文件:

import './index.scss'
export  const  GoodsItem =({id,goods_name,goods_img,goods_price,goods_count,goods_state,getState,addNum,reduceNum})=>{
    return (
        <div className="cart-goods-item">
      <div className="left">
        <div className="custom-control custom-checkbox">
          <input type="checkbox" checked={goods_state} onChange={()=>{}} className="custom-control-input" onChange={(e)=>getState(id,e.target.checked)} id={`"input${id}"`} />
          <label className="custom-control-label" htmlFor={`"input${id}"`}>
            <img
              src={goods_img}
              alt=""
            />
          </label>
        </div>
      </div>
      <div className="right">
        <div className="top">{goods_name}</div>
        <div className="bottom">
          <span className="price">¥ {goods_price*goods_count}</span>
          <span>
            <button onClick={()=>addNum(id,goods_count)}>+</button>
            <button>{goods_count}</button>
            <button onClick={()=>reduceNum(id,goods_count)}>-</button>
          </span>
        </div>
      </div>
    </div>
    )
}

GoodsItem下的index.scss文件:

.cart-goods-item {
    display: flex;
    padding: 10px;
    border-bottom: 1px solid #ccc;
    .left {
      img {
        width: 120px;
        height: 120px;
        margin-right: 8px;
        border-radius: 10px;
      }
      .custom-control-label::before,
      .custom-control-label::after {
        top: 50px;
      }
    }
    .right {
      flex: 1;
      display: flex;
      flex-direction: column;
      justify-content: space-between;
      .bottom {
        display: flex;
        justify-content: space-between;
        padding: 5px 0;
        .price {
          color: red;
          font-weight: bold;
        }
      }
    }
  }

CarFooter下的index.jsx文件:

import './index.scss'
export const CarFooter=({setcheckAll,checkAll,totalPrice,totalCount})=>{
    return (
        <div className="cart-footer">
      <div className="custom-control custom-checkbox">
        <input
          type="checkbox"
          className="custom-control-input"
          id="footerCheck"
          checked={checkAll}
          onChange={(e)=>setcheckAll(e.target.checked)}
        />
        <label className="custom-control-label" htmlFor="footerCheck">
          全选
        </label>
      </div>
      <div>
        <span>合计:</span>
        <span className="price">¥ {totalPrice}</span>
      </div>
      <button type="button" className="footer-btn btn btn-primary">
        结算 ({totalCount})
      </button>
    </div>
    )
}

CarFooter下的indexscss文件:

.cart-footer {
  z-index: 999;
  position: fixed;
  bottom: 0;
  width: 100%;
  height: 50px;
  border-top: 1px solid #ccc;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 10px;
  background: #fff;

  .price {
    color: red;
    font-weight: bold;
    font-size: 15px;
  }
  .footer-btn {
    min-width: 80px;
    height: 30px;
    line-height: 24px;
    border-radius: 25px;
    padding: 0;
  }
}

React中没有像Vue.js中计算属性的概念,但可以通过以下方法实现类似计算属性的功能: 1. 使用getter方法 在组件中,我们可以定义一个getter方法来计算属性。getter方法是一个函数,它根据组件的state或props计算属性的值,并返回该值。当state或props发生变化时,getter方法会重新计算属性的值。在组件使用该属性时,直接调用getter方法即可。 示例代码: ``` class MyComponent extends React.Component { get fullName() { return this.props.firstName + ' ' + this.props.lastName; } render() { return <div>Full Name: {this.fullName}</div>; } } ``` 2. 使用高阶组件 高阶组件是一个函数,接收一个组件作为参数,并返回一个新的组件。我们可以编写一个高阶组件,该组件接收一个组件和计算属性的方法作为参数,并返回一个新的组件。新的组件可以在render方法中调用计算属性的方法,来获取计算属性的值。 示例代码: ``` function withFullName(WrappedComponent) { return class extends React.Component { get fullName() { return this.props.firstName + ' ' + this.props.lastName; } render() { return <WrappedComponent {...this.props} fullName={this.fullName} />; } }; } class MyComponent extends React.Component { render() { return <div>Full Name: {this.props.fullName}</div>; } } const MyComponentWithFullName = withFullName(MyComponent); ``` 在上面的示例中,withFullName函数是一个高阶组件,它接收一个组件作为参数,并返回一个新的组件。新的组件中定义了fullName计算属性的方法,并将该属性作为prop传递给原始组件MyComponent。在MyComponent中,我们可以通过this.props.fullName访问fullName计算属性的值。 这些方法都可以实现类似计算属性的功能,您可以根据自己的需求选择适合的方
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值