กลับไปหน้าบทความ
#Next.js#ตะกร้าสินค้า#localStorage#Server State#React

วิธีเก็บจำนวนสินค้าในตะกร้าบน Next.js ให้เหมาะกับแต่ละงาน

การเก็บจำนวนสินค้าในตะกร้าบน Next.js มีหลายแนวทาง ขึ้นอยู่กับว่าต้องการให้ข้อมูลคงอยู่ได้นานแค่ไหน และจำเป็นต้องซิงก์ข้ามอุปกรณ์หรือไม่ โดยหลักสำคัญคือควรยึดรายการสินค้าเป็นแหล่งข้อมูลหลัก และคำนวณจำนวนรวมจากรายการนั้นเส

19 มกราคม 2569อ่านประมาณ 2 นาที

แชร์บทความ

วิธีเก็บจำนวนสินค้าในตะกร้าบน Next.js ให้เหมาะกับแต่ละงาน

วิธีเก็บจำนวนสินค้าในตะกร้าบน Next.js ให้เหมาะกับแต่ละงาน

การทำระบบตะกร้าสินค้าใน Next.js อาจดูเป็นเรื่องเล็ก แต่ในทางปฏิบัติกลับมีรายละเอียดที่ส่งผลต่อทั้งประสบการณ์ใช้งาน ความถูกต้องของข้อมูล และความยืดหยุ่นของระบบในระยะยาว โดยเฉพาะเรื่อง "จำนวนสินค้าในตะกร้า" ที่มักถูกนำไปแสดงเป็น badge บน Navbar หรือส่วนสรุปคำสั่งซื้อ

แนวทางที่เหมาะสมไม่ได้มีแค่แบบเดียว เพราะต้องพิจารณาว่าตะกร้าควรอยู่ได้นานแค่ไหน ต้องอยู่หลังรีเฟรชหน้าหรือไม่ และจำเป็นต้องใช้งานต่อเนื่องข้ามอุปกรณ์หรือหลายแท็บหรือเปล่า

เริ่มจาก Single Source of Truth

หลักคิดสำคัญที่สุดคือ ควรเก็บ "รายการสินค้าในตะกร้า" เป็นข้อมูลหลัก และให้ "จำนวนรวม" เป็นค่าที่คำนวณจากรายการเหล่านั้นเสมอ ไม่ควรแยกเก็บจำนวนรวมแบบอิสระโดยไม่จำเป็น

ตัวอย่างเช่น หากมีการเพิ่ม ลด หรือลบสินค้า ระบบควรอัปเดตเฉพาะรายการสินค้า แล้วคำนวณจำนวนรวมใหม่จากข้อมูลจริง วิธีนี้ช่วยลดโอกาสที่ข้อมูลจะหลุดซิงก์ เช่น จำนวน badge ไม่ตรงกับจำนวนสินค้าจริงในตะกร้า

ข้อดีของแนวทางนี้คือ:

  • ลดความผิดพลาดจากการอัปเดตหลายจุด
  • ทำให้ logic ชัดเจนและดูแลง่าย
  • รองรับการขยายไปยังยอดรวม ราคา หรือส่วนลดได้สะดวก

ทางเลือกยอดนิยมในการเก็บข้อมูลตะกร้าบน Next.js

1. เก็บใน Client State

วิธีที่ง่ายและเร็วที่สุดคือเก็บข้อมูลไว้ในฝั่ง client เช่นใช้ React Context, Zustand หรือ Redux โดยเก็บ cartItems ไว้ใน state แล้วคำนวณจำนวนรวมจากข้อมูลนั้น

แนวทางนี้เหมาะกับ:

  • ตะกร้าชั่วคราว
  • ผู้ใช้ที่ยังไม่ล็อกอิน
  • ระบบ MVP ที่ต้องการเริ่มต้นเร็ว

ข้อดี:

  • ตอบสนองเร็วมาก
  • ไม่ต้องพึ่ง backend ตั้งแต่แรก
  • โค้ดเริ่มต้นไม่ซับซ้อน

ข้อควรระวัง:

  • รีเฟรชหน้าแล้วข้อมูลอาจหาย
  • ไม่ซิงก์ข้ามแท็บหรือข้ามอุปกรณ์โดยอัตโนมัติ

2. เก็บใน localStorage

ถ้าต้องการให้ข้อมูลยังอยู่หลังรีเฟรชหน้าเว็บ การใช้ localStorage เป็นทางเลือกที่นิยมมาก โดยเมื่อมีการเพิ่มหรือลบสินค้า ให้ระบบอัปเดตทั้ง state และเขียนข้อมูลล่าสุดลงใน localStorage จากนั้นเมื่อผู้ใช้เปิดเว็บอีกครั้ง ค่อยอ่านค่ากลับมาเติมเข้า state

แนวทางนี้เหมาะกับ:

  • ร้านค้าออนไลน์ทั่วไป
  • ผู้ใช้ที่ใช้งานบนเครื่องเดิมเป็นหลัก
  • กรณีที่ยังไม่ต้องการระบบล็อกอินเต็มรูปแบบ

ข้อดี:

  • รีเฟรชแล้วข้อมูลยังอยู่
  • ติดตั้งและใช้งานไม่ยาก
  • เหมาะกับการพัฒนาเร็วในระดับ production เบื้องต้น

คำแนะนำเพิ่มเติม:

  • ควรตั้งชื่อ key แบบมีเวอร์ชัน เช่น cart_v1
  • หากเปลี่ยนโครงสร้างข้อมูลในอนาคต จะจัดการ migration ได้ง่ายขึ้น

3. เก็บบน Server หรือ Database ร่วมกับ Cookie/Session

หากต้องการให้ตะกร้าซิงก์ข้ามอุปกรณ์ หรือรองรับการใช้งานจริงจังในระบบที่มีผู้ใช้ล็อกอิน ควรเก็บข้อมูลตะกร้าไว้บน server หรือ database แล้วใช้ cookie หรือ session เพื่อระบุตัวตนของผู้ใช้หรือของตะกร้า

รูปแบบทั่วไปคือ:

  • ฝั่ง client เรียก API เพื่อเพิ่ม ลด หรือลบสินค้า
  • ฝั่ง server จัดเก็บข้อมูลตะกร้าในฐานข้อมูลหรือ session store
  • cookie เก็บเพียง session id หรือ cart id

แนวทางนี้เหมาะกับ:

  • ระบบที่ต้องการความถูกต้องสูง
  • ผู้ใช้ที่มีบัญชีล็อกอิน
  • งานที่ต้องการซิงก์หลายอุปกรณ์

ข้อดี:

  • ข้อมูลคงอยู่แม้เปลี่ยนเครื่อง
  • ควบคุมความถูกต้องได้ดีกว่า
  • รองรับ business logic ที่ซับซ้อนได้ง่าย

สิ่งที่มักพลาดเมื่อทำบน Next.js

หนึ่งในจุดที่นักพัฒนามักพลาดคือการอ่าน localStorage ระหว่าง Server Side Render ซึ่งทำไม่ได้ เพราะฝั่ง server ไม่มี window

ดังนั้น หากต้องอ่านข้อมูลจาก localStorage ควรทำในฝั่ง client เท่านั้น เช่นภายใน useEffect

อีกประเด็นสำคัญคือปัญหา hydration mismatch เช่น server เรนเดอร์ badge เป็นค่าหนึ่ง แต่ฝั่ง client อ่านข้อมูลจริงแล้วได้อีกค่าหนึ่ง ทำให้หน้าเว็บไม่ตรงกันในช่วงแรก

วิธีลดปัญหานี้ เช่น:

  • แสดงค่าเริ่มต้นเป็น 0 ก่อน
  • ค่อยอัปเดตค่าหลัง component mount
  • ใช้ skeleton หรือ placeholder ระหว่างรอข้อมูลจริง

โครงสร้างข้อมูลตะกร้าที่แนะนำ

รูปแบบที่จัดการง่ายและมีประสิทธิภาพคือการเก็บสินค้าเป็น map โดยอ้างอิงตาม productId เพราะช่วยให้ค้นหาและอัปเดตได้รวดเร็ว

ตัวอย่างแนวคิด:

items: {
  "p_101": { qty: 2, price: 129 },
  "p_305": { qty: 1, price: 499 }
}

จากโครงสร้างนี้สามารถคำนวณได้ทันทีว่า:

  • จำนวนรวม = ผลรวมของ qty ทุกสินค้า
  • ยอดรวม = ผลรวมของ qty x price

ข้อดีของโครงสร้างลักษณะนี้คือ:

  • อัปเดตจำนวนสินค้าได้เร็ว
  • คำนวณค่ารวมได้ชัดเจน
  • ขยายต่อไปยังโปรโมชั่นหรือการตรวจสอบสต็อกได้ง่าย

สูตรคำนวณจำนวนสินค้าอย่างปลอดภัย

แนวทางที่ควรใช้คือคำนวณจำนวนรวมด้วยการ reduce จากรายการสินค้าเสมอ แทนที่จะเก็บ totalQty แยกไว้โดยตรง

เหตุผลคือเมื่อมีหลาย action เช่น เพิ่มสินค้า ลดสินค้า ลบสินค้า หรือล้างตะกร้า หากเก็บ totalQty แยกไว้ จะต้องอัปเดตให้ถูกทุกกรณี ไม่เช่นนั้นข้อมูลอาจผิดเพี้ยนได้ง่าย

ดังนั้นแนวทางที่ปลอดภัยที่สุดคือ:

  • เก็บรายการสินค้าเป็นหลัก
  • คำนวณจำนวนรวมเมื่อจำเป็น
  • หากจำเป็นต้อง cache ค่า total จริง ๆ ต้องมั่นใจว่าอัปเดตครบทุก action

การใช้งานกับ App Router ใน Next.js 13+

หากใช้ App Router และต้องการให้ badge ตะกร้าอัปเดตได้ทุกหน้าอย่างสะดวก วิธีที่นิยมคือสร้าง CartProvider เป็น client component แล้วนำไปครอบไว้ที่ layout

จากนั้นให้ Navbar หรือ component อื่น ๆ อ่านจำนวนรวมจาก provider หรือ store โดยตรง

ข้อดีของแนวทางนี้คือ:

  • ไม่ต้องส่ง props ข้ามหลายชั้น
  • ทุกหน้าสามารถเข้าถึงข้อมูลตะกร้าได้ง่าย
  • ทำให้โครงสร้างโค้ดสะอาดและดูแลง่ายขึ้น

การซิงก์หลายแท็บด้วย localStorage

อีกเทคนิคที่มีประโยชน์มากแต่หลายคนมองข้ามคือการซิงก์ข้อมูลตะกร้าระหว่างหลายแท็บของเบราว์เซอร์ หากใช้ localStorage สามารถฟัง event storage ได้

เมื่อแท็บหนึ่งมีการเพิ่มหรือลบสินค้า อีกแท็บจะรับรู้การเปลี่ยนแปลงและอัปเดต badge ได้ทันที

เหมาะสำหรับกรณีที่ผู้ใช้เปิดหลายแท็บเพื่อเลือกสินค้าหลายหน้าพร้อมกัน ซึ่งช่วยให้ประสบการณ์ใช้งานสอดคล้องมากขึ้น

ป้องกันข้อมูลเพี้ยนจากการกดซ้ำหรือเน็ตช้า

ในระบบ production โดยเฉพาะแบบที่เก็บข้อมูลบน server ควรเตรียมรับมือกับสถานการณ์ที่ผู้ใช้กดปุ่มซ้ำเร็ว ๆ หรือเครือข่ายตอบสนองช้า

แนวทางที่แนะนำ ได้แก่:

  • ใช้ optimistic update ฝั่ง client เพื่อให้ UI ตอบสนองเร็ว
  • เมื่อ server ตอบกลับแล้วค่อย reconcile ข้อมูลให้ตรงกันอีกครั้ง
  • ใช้ idempotency key เพื่อป้องกันการส่งคำขอซ้ำจนเพิ่มสินค้าหลายครั้งโดยไม่ตั้งใจ

สิ่งเหล่านี้ช่วยให้ระบบดูเสถียรและลดปัญหาข้อมูลผิดพลาดในสถานการณ์จริง

เลือกแบบไหนดีให้เหมาะกับงาน

ถ้าต้องการตัดสินใจแบบรวดเร็ว สามารถใช้แนวทางนี้ได้

  • ถ้าเพิ่งเริ่มทำ MVP: ใช้ Client State ร่วมกับ localStorage
  • ถ้ามีระบบล็อกอินและต้องการใช้ข้ามอุปกรณ์: ใช้ Server/DB ร่วมกับ session
  • ถ้าระบบซับซ้อนและทำงานเป็นทีม: ใช้ Zustand พร้อม persist และเชื่อม API sync

การเลือกวิธีที่เหมาะสมตั้งแต่ต้นจะช่วยลดงานแก้โครงสร้างภายหลังได้มาก

แนวคิดนี้ไม่ได้ใช้ได้แค่กับตะกร้า

หลักการเดียวกันนี้สามารถนำไปปรับใช้กับฟีเจอร์อื่นได้เช่นกัน เช่น:

  • wishlist
  • compare list
  • รายการสินค้าที่ดูล่าสุด
  • รายการที่บันทึกไว้ชั่วคราว

เมื่อออกแบบให้รายการเป็นข้อมูลหลัก และให้ค่าต่าง ๆ เป็นผลลัพธ์จากการคำนวณ ระบบจะยืดหยุ่นและน่าเชื่อถือมากขึ้น

สรุป

การเก็บจำนวนสินค้าในตะกร้าบน Next.js ไม่มีคำตอบแบบเดียวสำหรับทุกโปรเจกต์ แต่มีหลักสำคัญที่ควรยึดร่วมกันเสมอ คือเก็บ "รายการสินค้า" เป็นแหล่งข้อมูลหลัก และคำนวณ "จำนวนรวม" จากรายการนั้นทุกครั้ง

หากต้องการความง่ายและเร็ว ใช้ Client State ได้เลย หากต้องการให้ข้อมูลอยู่หลังรีเฟรช เลือก localStorage และถ้าต้องการความถูกต้องสูงพร้อมซิงก์ข้ามอุปกรณ์ การเก็บข้อมูลบน Server หรือ Database จะเป็นทางเลือกที่เหมาะกว่า

เมื่อเลือกวิธีเก็บข้อมูลให้สอดคล้องกับเป้าหมายของระบบตั้งแต่ต้น จะช่วยให้ตะกร้าสินค้าทำงานได้เสถียร ใช้งานง่าย และพร้อมต่อยอดในระยะยาว