千峰React:案例二

完成对html文档还有css的引入,引入一下数据:

javascript 复制代码
import { func } from 'prop-types'
import './购物车样式.css'
import axios from 'axios'
import { useImmer } from 'use-immer'
import { useEffect } from 'react'

function Item() {
  return (
    <li className='active'>
      <h3>香蕉</h3>
      <p>单价:5</p>
      <p>
        数量:
        <span className='remove'>-</span>
        <span>1</span>
        <span className='add'>+</span>
      </p>
      <div className='cartbtn'>取消购买</div>
    </li>
  )
}

function Cart() {
  const [list, setList] = useImmer([])
  useEffect(() => {
    axios.get('../public/cartData.json').then((res) => {
      console.log(res)
    })
  })
  return (
    <div className='cart'>
      <ul>
        <Item />
      </ul>
      <div className='all'>
        总金额:<span>5</span>元
      </div>
    </div>
  )
}
export default Cart

然后判断数据是否存在:

刚刚数字一直在疯涨,导致他疯涨的原因是我的useEffect没加依赖数组,还有axios的url,如果我的数据文件在public里,可以直接从根目录访问(好像学过),要写成这样👇

javascript 复制代码
useEffect(() => {
    axios.get('/cartData.json').then((res) => {
        if (res.data.errcode === 0) {
            //map来给购物车添加
            setList(res.data.list.map((item) => ({...item,active:false})))
     }
    })
  },[])

还有一个无语的bug是箭头函数的return每次都会加大括号忘记加retuen,不加return又会格式化到下一行

对每个按钮绑上onClick事件,再给每个按钮绑上id:

javascript 复制代码
import { func } from 'prop-types'
import './购物车样式.css'
import axios from 'axios'
import { useImmer } from 'use-immer'
import { useEffect } from 'react'

function Item({id,name,price,number,active,handleAdd}) {
    return (
      //active初始根据active的值判定
      <li className={ active?'active':''}>
          <h3>{ name}</h3>
          <p>单价:{ price}</p>
      <p>
        数量:
        <span className='remove'>-</span>
              <span>{ number}</span>
        <span className='add'>+</span>
      </p>
            <div className='cartbtn' onClick={()=>handleAdd(id)}>{ active?'取消购买':'添加购物车'}</div>
    </li>
  )
}

function Cart() {
  const [list, setList] = useImmer([])
  useEffect(() => {
    axios.get('/cartData.json').then((res) => {
      if (res.data.errcode === 0) {
        //map来给购物车添加
        setList(res.data.list.map((item) => ({ ...item, active: false })))
      }
    })
  }, [])
    const handleAdd=(id) => {
        setList((draft) => {
            const value = draft.find((item) => item.id === id)
            value.active=!value.active
        })
    }
  return (
    <div className='cart'>
      <ul>
              {list.map((item) => <Item key={item.id} {...item} handleAdd={handleAdd} />)}
      </ul>
      <div className='all'>
        总金额:<span>5</span>元
      </div>
    </div>
  )
}
export default Cart

增加修改数量的功能

javascript 复制代码
import { func } from 'prop-types'
import './购物车样式.css'
import axios from 'axios'
import { useImmer } from 'use-immer'
import { useEffect } from 'react'

function Item({
  id,
  name,
  price,
  number,
  active,
  handleAdd,
  handleNumberChange,
}) {
  return (
    //active初始根据active的值判定
    <li className={active ? 'active' : ''}>
      <h3>{name}</h3>
      <p>单价:{price}</p>
      <p>
        数量:
        <span className='remove' onClick={()=>handleNumberChange(id, -1)}>
          -
        </span>
        <span>{number}</span>
        <span className='add' onClick={()=>handleNumberChange(id, +1)}>
          +
        </span>
      </p>
      <div className='cartbtn' onClick={() => handleAdd(id)}>
        {active ? '取消购买' : '添加购物车'}
      </div>
    </li>
  )
}

function Cart() {
  const [list, setList] = useImmer([])
  useEffect(() => {
    axios.get('/cartData.json').then((res) => {
      if (res.data.errcode === 0) {
        //map来给购物车添加
        setList(res.data.list.map((item) => ({ ...item, active: false })))
      }
    })
  }, [])
  const handleAdd = (id) => {
    setList((draft) => {
      const value = draft.find((item) => item.id === id)
      value.active = !value.active
    })
  }
  const handleNumberChange = (id, num) => {
    setList((draft) => {
      const value = draft.find((item) => item.id === id)
      if (value.number === 0 && num < 0) {
        return
      }
      value.number += num
    })
  }
  return (
    <div className='cart'>
      <ul>
        {list.map((item) => (
          <Item
            key={item.id}
            {...item}
            handleAdd={()=>handleAdd(item.id)}
            handleNumberChange={(num)=>handleNumberChange(item.id,num)}//注意传参问题,前面的num是onClick传递的
          />
        ))}
      </ul>
      <div className='all'>
        总金额:<span>5</span>元
      </div>
    </div>
  )
}
export default Cart

如果你写成 handleNumberChange={() => handleNumberChange(item.id, num)}num 的值无法动态传递,因为 num 是在 Item 组件的 onClick 事件中才确定的。

加入计算总金额功能,用filter选择被加入购物车的商品,reduce计算

javascript 复制代码
 const all = list
    .filter((item) => item.active)
    .reduce((init, item) => init + item.number * item.price, 0)

每次改变状态时都会重新渲染,提升性能,使用memo,只有props不同的时候才会渲染

里面包的是函数,之前学的useCallback可以解决函数被Object.Is()判别为不同的问题

javascript 复制代码
import { func } from 'prop-types'
import './购物车样式.css'
import axios from 'axios'
import { useImmer } from 'use-immer'
import { memo, useCallback, useEffect } from 'react'

const Item = memo(function Item({
  id,
  name,
  price,
  number,
  active,
  handleAdd,
  handleNumberChange,
}) {
  console.log('如果被渲染了,我就会出现')

  return (
    //active初始根据active的值判定
    <li className={active ? 'active' : ''}>
      <h3>{name}</h3>
      <p>单价:{price}</p>
      <p>
        数量:
        <span className='remove' onClick={() => handleNumberChange(id, -1)}>
          -
        </span>
        <span>{number}</span>
        <span className='add' onClick={() => handleNumberChange(id, +1)}>
          +
        </span>
      </p>
      <div className='cartbtn' onClick={() => handleAdd(id)}>
        {active ? '取消购买' : '添加购物车'}
      </div>
    </li>
  )
})

function Cart() {
  const [list, setList] = useImmer([])
  const all = list
    .filter((item) => item.active)
    .reduce((init, item) => init + item.number * item.price, 0)
  useEffect(() => {
    axios.get('/cartData.json').then((res) => {
      if (res.data.errcode === 0) {
        //map来给购物车添加
        setList(res.data.list.map((item) => ({ ...item, active: false })))
      }
    })
  }, [])
  const handleAdd = useCallback((id) => {
    setList((draft) => {
      const value = draft.find((item) => item.id === id)
      value.active = !value.active
    })
  })
  const handleNumberChange = useCallback((id, num) => {
    setList((draft) => {
      const value = draft.find((item) => item.id === id)
      if (value.number === 0 && num < 0) {
        return
      }
      value.number += num
    })
  })
  return (
    <div className='cart'>
      <ul>
        {list.map((item) => (
          <Item
            key={item.id}
            {...item}
            handleAdd={() => handleAdd(item.id)}
            handleNumberChange={(num) => handleNumberChange(item.id, num)} //注意传参问题,前面的num是onClick传递的
          />
        ))}
      </ul>
      <div className='all'>
        总金额:<span>{all}</span>元
      </div>
    </div>
  )
}
export default Cart

这下基本就写完了

相关推荐
颜酱2 小时前
二叉树分解问题思路解题模式
javascript·后端·算法
炫饭第一名3 小时前
速通Canvas指北🦮——路径与形状篇
前端·javascript·程序员
无责任此方_修行中3 小时前
如何利用 pnpm 的安全控制功能防御 npm 供应链攻击
javascript·npm·node.js
进击的尘埃3 小时前
前端状态管理的本质:从 Vuex 到 Pinia,我们到底在管理什么?
javascript
码路飞3 小时前
GPT-5.3 Instant 终于学会好好说话了,顺手对比了下同天发布的 Gemini 3.1 Flash-Lite
java·javascript
Lee川3 小时前
从回调地狱到同步之美:JavaScript异步编程的演进之路
javascript·面试
进击的尘埃3 小时前
WebSocket 长连接方案设计:从心跳保活到断线重连的生产级实践
javascript
摸鱼的春哥5 小时前
Agent教程15:认识LangChain(中),状态机思维
前端·javascript·后端
明月_清风6 小时前
告别遮挡:用 scroll-padding 实现优雅的锚点跳转
前端·javascript
明月_清风6 小时前
原生 JS 侧边栏缩放:从 DOM 监听到底层优化
前端·javascript