不使用 redux
实现 购物车案例
使用 React 自带的 useState 和 useEffect
Hook 即可实现购物车
export default function ShoppingCar() {
// 要结算的商品 总数 以及总价
const [totalNum, setTotalNum] = useState(0)
const [totalPerice, setTotalPerice] = useState(0)
// 商品列表 初始化一个mock 数据
const [list, setList] = useState(orderLists)
...
}
1、新增商品
使用 setList()
进数据状态更新
// 模拟新增商品
const handleAdd = () => {
const curList = [...list]
console.log('===curList=', curList)
curList.unshift({
name: `商品${list.length + 1}`,
id: `sss_${list.length + 1}` ,
num: 1,
price: 12.00,
totalPrice: 12.00,
})
setList([...curList])
}
2、修改商品的数量
const handleChangeNum = (type, id) => {
const curList = [...list]
if(type === 'ADD') {
curList.map(itm => {
if (itm.id === id) {
itm.num = itm.num + 1
itm.totalPrice = itm.num * itm.price
}
})
} else{
curList.map(itm => {
if (itm.id === id) {
itm.num = itm.num !== 0 ? itm.num - 1 : 0
itm.totalPrice = itm.num * itm.price
}
})
}
setList([...curList])
}
3、选择要结算的商品
const handleChangeCheckbox = (e, id) =>{
console.log('---', e.target.value)
const curList = [...list]
curList.map(itm => {
if (itm.id === id) {
itm.isChecked = !itm.isChecked
}
})
setList([...curList])
}
4、计算要结算商品的 总数 及 总价
使用 useEffect()
监测 商品列表的变化
useEffect(() => {
let isSelectedLists = []
list.map(itm => {
if (itm.isChecked) {
isSelectedLists.push(itm)
}
})
console.log('=isSelectedLists==', isSelectedLists)
const curNum = isSelectedLists && isSelectedLists.length &&isSelectedLists.reduce((total, item) => total + item.num, 0) || 0
const curTotal = isSelectedLists && isSelectedLists.length && isSelectedLists.reduce((total, item) => total + item.totalPrice, 0) || 0
console.log('==curNum==', curNum)
console.log('==curTotal==', curTotal)
setTotalNum(curNum)
setTotalPerice(curTotal)
}, [list]
5、完整代码案例
// index.jsx
import React, {useState, useEffect, useId} from 'react'
import './index.scss'
import { orderLists } from './mock.js'
export default function ShoppingCar() {
// const id = useId()
const [totalNum, setTotalNum] = useState(0)
const [totalPerice, setTotalPerice] = useState(0)
const [list, setList] = useState(orderLists)
const handleChangeNum = (type, id) => {
const curList = [...list]
if(type === 'ADD') {
curList.map(itm => {
if (itm.id === id) {
itm.num = itm.num + 1
itm.totalPrice = itm.num * itm.price
}
})
} else{
curList.map(itm => {
if (itm.id === id) {
itm.num = itm.num !== 0 ? itm.num - 1 : 0
itm.totalPrice = itm.num * itm.price
}
})
}
setList([...curList])
}
const handleChangeCheckbox = (e, id) =>{
console.log('---', e.target.value)
const curList = [...list]
curList.map(itm => {
if (itm.id === id) {
itm.isChecked = !itm.isChecked
}
})
setList([...curList])
}
const handleAdd = () => {
const curList = [...list]
console.log('===curList=', curList)
curList.unshift({
name: `商品${list.length + 1}`,
id: `sss_${list.length + 1}` ,
num: 1,
price: 12.00,
totalPrice: 12.00,
})
setList([...curList])
}
useEffect(() => {
let isSelectedLists = []
list.map(itm => {
if (itm.isChecked) {
isSelectedLists.push(itm)
}
})
console.log('=isSelectedLists==', isSelectedLists)
const curNum = isSelectedLists && isSelectedLists.length &&isSelectedLists.reduce((total, item) => total + item.num, 0) || 0
const curTotal = isSelectedLists && isSelectedLists.length && isSelectedLists.reduce((total, item) => total + item.totalPrice, 0) || 0
console.log('==curNum==', curNum)
console.log('==curTotal==', curTotal)
setTotalNum(curNum)
setTotalPerice(curTotal)
}, [list])
return (
<div className='list'>
{
list.map(itm => {
return (
<div className="li" key={itm.id}>
<div className='commodity'>
<input type="checkbox" name="" id="" value={itm.isChecked} onClick={(e) => handleChangeCheckbox(e, itm.id)}/>
<span>{itm.name}</span>
</div>
<div className="price">单价:{itm.price}</div>
<div className='num'>
<span className='handle-icon' onClick={() => handleChangeNum('ADD', itm.id)}>+</span>
<span className='itm-num'>{itm.num}</span>
<span className='handle-icon' onClick={() => handleChangeNum('REDUCE', itm.id)}>-</span>
</div>
<div className='total'>总价:{itm.totalPrice}</div>
</div>
)
})
}
<div className='total'>
<span className='total-num'>共计:{totalNum}件</span>
<span className='total-price'>合计:{totalPerice}元</span>
</div>
<button className="btn" onClick={handleAdd}>增加商品</button>
</div>
)
}
// index.scss 文件
.li{
width: 100%;
height: auto;
display: flex;
align-items: left;
justify-content: left;
}
.commodity{
width: 100%;
height: 46px;
display: flex;
align-items: center;
justify-content: left;
&>span{
display: inline-block;
min-width: 100px;
margin-left: 6px;
margin-right: 6px;
}
}
.price{
display: inline-block;
margin-left: 6px;
margin-right: 6px;
min-width: 90px;
}
.num{
width: 100%;
height: 36px;
display: flex;
align-items: center;
cursor: pointer;
}
.handle-icon{
display: inline-block;
width: 36px;
height: 36px;
cursor: pointer;
line-height: 36px;
border: 1px solid #efefef;
}
.itm-num{
display: inline-block;
padding: 2px 8px;
}
.total{
min-width: 100px;
color: #ff0000;
}
.total-price{
margin-left: 20px;
}
.btn{
}
6、注意事项
a、使用useState
的setXXX()
函数进行修改 对象类型时候,需要修改其指针和值,如果不修改指向,不会触发视图更新
;
b、列表中每一项需要有唯一性的key
,这样才会触发唯一性进行数据更新;
c、使用useState 和 useEffect
实现的购物车,只能在当前页使用,不利于代码复用
,并且会随着 购物车里面优惠券、折扣等逻辑的增加,而变的难以维护;