-
Notifications
You must be signed in to change notification settings - Fork 0
Project 8 Array of Carts
สมมติว่าเรากำลังทำการสร้างระบบร้านค้า เรามี Product และเรากำลังสร้างรถเข็น (Cart) เพื่อเก็บ Product เอาไว้ซื้อของ
- สร้าง State ของ Cart เอาไว้สำหรับเก็บสินค้าในตะกร้า
const [cart,setCart] = useState([])2.ในสินค้า เมื่อมีการกดปุ่ม เพิ่มลงตะกร้า ให้ push เข้าไปใน State ของ cart แล้วลองแสดง Log ออกมาดู
<Button
size="sm"
color="primary"
onClick={() => {
cart.push(each)
setCart(cart)
console.log(cart)
}}
>
เพิ่ม
</Button>- สร้างลิสต์ของสินค้าที่อยู่ในตะกร้าและสร้างปุ่มลบ โดยเราจะเอา Index ออกมาด้วย โดยใน
_.mapจะมี index ออกมาให้ เป็น parameter ที่ 2 เสมอ
<div>
<div>สินค้าในตะกร้า</div>
{_.map(cart, (each, index) => (
<div>
{each?.name}{' '}
<Button color="danger" onClick={() => {}}>
ลบ
</Button>
</div>
))}
</div>- ลองสร้างฟังก์ชันจากการลบ โดยปกติ เราจะเอา Index ของ Array มาใช้ในการลบ โดยเราจะใช้ฟังก์ชัน splice ในการดึงข้อมูลใน index ใด index หนึ่งออกจาก Array Document ของ Splice โดยเราจะลบจาก Index ตัวนั้น และลบไป 1 ตัว
<Button onClick={() => {
cart.splice(index, 1);
setCart(cart);
console.log(cart);
}}>
ลบ
</Button>จะเห็นว่าใน Console จะทำงานได้อย่างถูกต้อง แต่ใน UI จะไม่เป็นตามนั้น ดังนั้นเพราะการเปลี่ยนค่าแบบการใส่ใน Array ระบบจะไม่มองว่าค่ามันเปลี่ยน ถ้าสมมติมีการเปลี่ยน state จากเลข 1 เป็นเลข 2 ระบบจะ Detect ได้ว่า State มันเปลี่ยนไป แต่ถ้าเปลี่ยนจาก Array หนึ่งเช่น [] ไปเป็น Array อีกอันหนึ่ง เช่น [{}] ระบบ จะไม่ได้ Detect ว่ามันเปลี่ยน เราจะต้องทำ State ตัวหนึ่งมาเพื่อหลอกตัวระบบ
- สร้าง state rerender ขึ้นมา โดยให้เป็น Boolean โดยเราจะใช้คำอะไรก็ได้ และค่ามันไม่ได้มีผล
const [rerender,setRerender] = useState(false)- ในทุกๆ ครั้งที่มีการเพิ่ม หรือ การลบ ให้มีการเปลี่ยน State Rerender เป็นตรงข้ามด้วย
<Button
size="sm"
color="primary"
onClick={() => {
cart.push(each)
setCart(cart)
console.log(cart)
setRerender(!rerender)
}}>
เพิ่ม
</Button>และในปุ่มลบ
<Button
onClick={() => {
cart.splice(index, 1)
setCart(cart)
console.log(cart)
setRerender(!rerender)
}}
>
ลบ
</Button>- จัด UI ต่างๆ ให้สวยงาม
สมมติว่าเรากดเพิ่ม ที่สินค้า หรือ Object เดิม ซ้ำ 2 ครั้ง จะพบว่าสินค้านั้น จะขึ้นมา 2 Record ซึ่งก็ไม่ได้ผิด แต่เราจะพัฒนาให้ดีขึ้น เพื่อให้ระบบมันบันทึกเป็นสินค้าเดียว แต่จำนวนเป็น 2 เราจะดำเนินการดังต่อไปนี้
- เราจะปรับ Payload ของตัวที่เราจะ Push เข้าไป แทนที่เราใช้ product ทั้งตัวไปเป็น object ตัวใหม่
<Button
size="sm"
color="primary"
onClick={() => {
cart.push({ product:each,quantity:1 })
setCart(cart)
console.log(cart)
setRerender(!rerender)
}}>
เพิ่ม
</Button>โดย จากเดิมเป็นเพียง each อย่างเดียว ตอนนี้เป็น {product:each, quantity:1} ดังรูป

- เราต้องเปลี่ยนการแสดงผล ที่จากเดิมเราแสดงชื่อ Product จาก
each.nameได้เลย เป็นeach.product.nameเพราะตอนนี้ข้อมูลของ Product ได้เข้าไปอยู่ใน attribute ที่ชื่อ product ไปแล้ว
<div>
<div>สินค้าในตะกร้า</div>
{_.map(cart, (each, index) => (
<div>
{each?.product?.name}{' '}
<Button color="danger" onClick={() => {
cart.splice(index, 1)
setCart(cart)
setRerender(!rerender)}}>
ลบ
</Button>
</div>
))}
</div>- เพิ่มเงื่อนไข ถ้าหากเจอ Product ชิ้นเดิม ให้บวก quantity เพิ่มอีก 1 โดยเราจะหาว่า Product นั้นเป็น Product ชิ้นเดิมมั้ย ด้วยการใช้
_.find()เป็นตัวช่วย ก่อนที่เราจะเพิ่ม โดยเราจะสร้างตัวแปรสำหรับเก็บผลการค้นหามาก่อน
<Button
size="sm"
color="primary"
onClick={() => {
const selectedProduct = _.find()
cart.push({ product:each,quantity:1 })
setCart(cart)
console.log(cart)
setRerender(!rerender)
}}>
เพิ่ม
</Button>- ใน Find จะคล้ายกับการ Map เราสามารถกระจายข้อมูลออกมา แล้วใส่เงื่อนไขได้ โดยเราจะใช้เงื่อนไขที่ Product ID เท่ากันกับ Product ที่เราจะใส่ไปใหม่
<Button
size="sm"
color="primary"
onClick={() => {
const selectedProduct = _.find(cart, eachProductInCard => eachProductInCard?.product?._id === each?._id)
cart.push({ product:each,quantity:1 })
setCart(cart)
console.log(cart)
setRerender(!rerender)
}}>
เพิ่ม
</Button>ถ้าไม่เข้าใจลองมองจากรูป สมมติว่า ในขณะนี้ก่อนที่เราจะ push เข้าไป เรากำลังจะกด Productชิ้นที่ 2 ในขณะที่ใน Cart มี product 3 ตัว

ในการใช้ Find มันจะทำเป็นรอบๆ ไป จนกว่าจะเจอ ถ้าเจอก็จะเอาอันแรกออกมาเลย

- ถ้าเจอ ให้เปลี่ยนค่าปริมาณเป็น quantity เดิม บวกด้วย 1 ถ้าไม่เจอก็ให้เป็นไปตาม Flow เดิมของมัน
<Button
size="sm"
color="primary"
onClick={() => {
const selectedProduct = _.find(cart, eachProductInCard => eachProductInCard?.product?._id === each?._id)
if (selectedProduct ){
selectedProduct.quantity = selectedProduct.quantity + 1
}
else {
cart.push({ product:each,quantity:1 })
}
setCart(cart)
console.log(cart)
setRerender(!rerender)
}}>
เพิ่ม
</Button>- แต่มันจะยังไม่เปลี่ยน เราจะต้องไปหา Index ว่า Product ที่เราเอามานี้มันอยู่ใน Index ไหน แล้วเปลี่ยนข้อมูล Index นั้น ด้วย
selectedProductตัวใหม่ โดยเราจะใช้การหา Index โดยใช้ findIndex
<Button
size="sm"
color="primary"
onClick={() => {
const selectedProduct = _.find(cart, eachProductInCard => eachProductInCard?.product?._id === each?._id)
const selectedProductIndex = _.findIndex(cart, eachProductInCard => eachProductInCard?.product?._id === each?._id)
if (selectedProduct ){
selectedProduct.quantity = selectedProduct.quantity + 1
cart[selectedProductIndex] = selectedProduct
}
else {
cart.push({ product:each,quantity:1 })
}
setCart(cart)
console.log(cart)
setRerender(!rerender)
}}>
เพิ่ม
</Button>- ลองแสดงปริมาณที่เพิ่มเข้าไป
<div>
<div>สินค้าในตะกร้า</div>
{_.map(cart, (each, index) => (
<div>
{each?.product?.name}{' '} ปริมาณ {each?.quantity}
<Button color="danger" onClick={() => {
cart.splice(index, 1)
setCart(cart)
setRerender(!rerender)}}>
ลบ
</Button>
</div>
))}
</div>