Skip to content

Project 8 Array of Carts

Theethawat Savastham edited this page May 23, 2024 · 10 revisions

สมมติว่าเรากำลังทำการสร้างระบบร้านค้า เรามี Product และเรากำลังสร้างรถเข็น (Cart) เพื่อเก็บ Product เอาไว้ซื้อของ

สร้าง Carts

  1. สร้าง 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>
  1. สร้างลิสต์ของสินค้าที่อยู่ในตะกร้าและสร้างปุ่มลบ โดยเราจะเอา Index ออกมาด้วย โดยใน _.map จะมี index ออกมาให้ เป็น parameter ที่ 2 เสมอ
 <div>
          <div>สินค้าในตะกร้า</div>
          {_.map(cart, (each, index) => (
            <div>
              {each?.name}{' '}
              <Button color="danger" onClick={() => {}}>
                ลบ
              </Button>
            </div>
          ))}
        </div>
  1. ลองสร้างฟังก์ชันจากการลบ โดยปกติ เราจะเอา 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 ตัวหนึ่งมาเพื่อหลอกตัวระบบ

  1. สร้าง state rerender ขึ้นมา โดยให้เป็น Boolean โดยเราจะใช้คำอะไรก็ได้ และค่ามันไม่ได้มีผล
  const [rerender,setRerender] = useState(false)
  1. ในทุกๆ ครั้งที่มีการเพิ่ม หรือ การลบ ให้มีการเปลี่ยน 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>
  1. จัด UI ต่างๆ ให้สวยงาม

การเปลี่ยนให้ Payload ของการ Push เพื่อให้เก็บจำนวนที่เลือก

สมมติว่าเรากดเพิ่ม ที่สินค้า หรือ Object เดิม ซ้ำ 2 ครั้ง จะพบว่าสินค้านั้น จะขึ้นมา 2 Record ซึ่งก็ไม่ได้ผิด แต่เราจะพัฒนาให้ดีขึ้น เพื่อให้ระบบมันบันทึกเป็นสินค้าเดียว แต่จำนวนเป็น 2 เราจะดำเนินการดังต่อไปนี้

  1. เราจะปรับ 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} ดังรูป image

  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>
  1. เพิ่มเงื่อนไข ถ้าหากเจอ 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>
  1. ใน 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 ตัว

image

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

  1. ถ้าเจอ ให้เปลี่ยนค่าปริมาณเป็น 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>
  1. แต่มันจะยังไม่เปลี่ยน เราจะต้องไปหา 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>
  1. ลองแสดงปริมาณที่เพิ่มเข้าไป
 <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>

General

Project 1 Familiar with React

Project 2 Tailwind CSS, Component and Layouting

Project 3 useState, useEffect, React Hook and API Call

Project 4 Basic Express, API and Database Access

Project 5 Frontend Backend Integration

Project 6 Routing

Project 7 Search with react

Project 8 Pushing the Array

Project 9 More complex data structure

Project 10 Mongo Aggregate Pipeline

Project 11 Create Pagination and Search

Clone this wiki locally