หน้าหลัก/บทเรียนขั้นสูง/7. การจัดการชุดข้อมูล Array
เวลาเรียนโดยประมาณ: 30 นาที
Chapter 7: Array Operations & Immutability

การจัดการข้อมูล Array ปั๊มการ์ดและคัดกรองแบบติดสปีด!

เบื่อไหมกับการเขียนโค้ดแสดงรายชื่อทีละบรรทัด? ในโลกความเป็นจริง ข้อมูลไม่ได้มาทีละชิ้น แต่มาเป็น ชุดข้อมูล (Array) ขนาดมหึมา! บทนี้จะพาผู้เรียนไปไขรหัสลับพลังของสองฟังก์ชันระดับโลกอย่าง .map() และ .filter() ที่เป็นกระดูกสันหลังของการวาดลิสต์บน React

JavaScript Array Map and Filter Conveyor Belt Factory Illustration

พจนานุกรมปูพื้นฐานการจัดการ Array (Array Dictionary)

ปูพื้นฐานศัพท์เทคนิคสำคัญเพื่อเปลี่ยนและจัดการชุดข้อมูลอาร์เรย์อย่างคล่องแคล่วและถูกต้อง:

1. Array (ชุดข้อมูลเรียงแถว)

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

2. .map() (เมธอดแปรรูปสมาชิก)

คำสั่งเดินตรวจสมาชิกทุกตัวในแถวเพื่อนำไปแปลงโฉมฉีดส่วนผสมไอซิ่งความงาม แล้วคายสระสร้างชิ้นงานชุดใหม่ที่มีขนาดแถวเท่าเดิม (ใช้ปั๊ม Component การ์ดต่างๆ)

3. .filter() (เมธอดดักกรองชิ้นงาน)

คำสั่งดักตรวจสมาชิกทีละชิ้นโดยส่งเข้าเกณฑ์เปรียบเทียบ หากชิ้นใดตอบผลลัพธ์ผ่านเกณฑ์ (true) จะมีสิทธิ์เดินทางต่อ ส่วนชิ้นที่ไม่ผ่าน (false) จะถูกคัดกรองสลัดทิ้ง

4. Immutability (ความไม่กลายพันธุ์ของอาร์เรย์)

กฎเหล็ก React ที่ห้ามเข้าไปแก้ไขของในอาร์เรย์ State ตรงๆ (เช่น ห้ามใช้ push, splice) แต่ต้องใช้วิธีกางก๊อปปี้ชิ้นงานเก่ามาปะปนกับชิ้นใหม่ในอาร์เรย์ใหม่เอี่ยมแทน

1เมื่อ HTML ห้ามเขียนวนลูป for ใน React! (The JSX Loop Barrier)

ถ้าคุณคุ้นเคยกับภาษาโปรแกรมมิ่งแบบดั้งเดิม เช่น JavaScript ฝั่งหลังบ้าน (Node.js), C++, หรือ Python เวลาเราอยากจะสั่งให้คอมพิวเตอร์พิมพ์รายการสินค้า 10 ชิ้นออกหน้าจอ สิ่งแรกที่เราจะนึกถึงคือการใช้ลูป for หรือ while ใช่หรือไม่?

แต่ในโลกของ React และ Next.js ที่มีโครงสร้างโค้ดหน้าตาเหมือน HTML (ที่เรียกว่า JSX) มีกฎเหล็กที่สำคัญมากคือ: "ห้ามนำลูป for ไปเขียนไว้ในวงเล็บปีกกาของ JSX โดยตรงเด็ดขาด!"

❌ เขียนแบบนี้พังแน่นอน (Syntax Error!)
return (
  <ul>
    {/* 💥 ใช้ลูป for ตรงนี้ไม่ได้! */}
    {for (let i = 0; i < items.length; i++) {
      return <li>{items[i]}</li>
    }}
  </ul>
);

เบราว์เซอร์จะประท้วงและขึ้นตัวแดงหนาเตอะ เพราะคุณพยายามเอาคำสั่งควบคุมการไหล (Control Flow) ไปพ่นกลางบล็อก HTML

✔️ เขียนแบบนี้ผ่านฉลุยสไตล์มือโปร
return (
  <ul>
    {/* ✨ ใช้ .map() แปลงร่างชุดอาเรย์ */}
    {items.map((item, index) => (
      <li key={index}>{item}</li>
    ))}
  </ul>
);

React จะยิ้มและพ่นผลลัพธ์การ์ดสินค้าออกมาเรียงกัน 10 ชิ้นทันทีในเสี้ยววินาทีโดยปราศจากข้อผิดพลาด

เจาะลึกความต่าง: Statement (คำสั่งกระทำ) VS Expression (นิพจน์คายค่า)

สาเหตุที่เป็นแบบนี้เพราะว่า ภายใต้เครื่องหมายปีกกา { } ใน JSX ของ React นั้น รองรับเฉพาะ "นิพจน์ที่สามารถคายตัวแปรชุดเดียวออกมาได้ทันที" (Expressions) เท่านั้น

Statements (คำสั่งกระทำ):

เช่น for, if-else, switch สิ่งเหล่านี้เป็นเหมือน "คัมภีร์ระบุการกระทำ" ที่ทำงานทีละขั้นตอนเป็นบล็อกๆ แต่ไม่มีน้ำพุคายผลลัพธ์ที่เป็นตัวแปรหรือค่าหน้าจอชิ้นเดียวยื่นออกมาในประโยค

Expressions (นิพจน์คายค่า):

เช่น ฟังก์ชัน .map(), หรือ Ternary operator (เช่น x ? y : z) สิ่งเหล่านี้ทำงานเสร็จแล้วจะ ยุบตัวลงเหลือผลลัพธ์ชิ้นเดียวทันที เช่น .map() ทำงานเสร็จปุ๊บ จะคายอาเรย์ของ HTML ออกมา 1 ก้อนทันที ซึ่ง React ชอบมากๆ เพราะนำไปวาดหน้าจอต่อได้เลย

พระเอกตัวจริงของเรา: array.map()

เครื่องมือที่มาเป็นสะพานเชื่อมให้เราสามารถ "วนลูปแบบ Expression" ได้ก็คือ .map()มันจะทำหน้าที่เสมือน "โรงงานสแตมป์หน้าจอ" ที่จะหยิบข้อมูลดิบทุกๆ ชิ้นที่เก็บอยู่ในกล่องอาเรย์มาประทับตราใส่เสื้อผ้า (เป็น Component การ์ดหรือรายการลิสต์) แล้วยื่นออกมาส่งให้ React วาดในรวดเดียว!

2.map() — เครื่องปั๊ม Component จากข้อมูลดิบ

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

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

ภาพการจำลองการทำงานของ .map()
["🍩", "🥐", "🍰"]
.map( item => <li>{item}</li> )
[<li>🍩</li>, <li>🥐</li>, <li>🍰</li>]

ลองดูตัวอย่างการแปลงชุดข้อมูลดิบ (Array of Objects) ไปเป็นการ์ดสินค้า Component ในชีวิตจริง:

การ map ข้อมูลออบเจกต์เข้าสู่ Component
const bakeryStore = [ { id: "1", name: "🍩 โดนัท", price: 49 }, { id: "2", name: "🥐 ครัวซองต์", price: 65 }, { id: "3", name: "🍰 ชีสเค้ก", price: 85 } ]; return ( <div className="grid"> {bakeryStore.map((item) => ( // เราปั๊ม Component การ์ดสินค้าและส่งข้อมูลดิบเข้าไปเป็น Props! <BakeryCard key={item.id} title={item.name} price={item.price} /> ))} </div> );

⚠️ ข้อผิดพลาดคลาสสิกของมือใหม่: ลืมคำว่า return หรือใช้ปีกกาผิดแบบ!

หลายครั้งที่เขียนโค้ดแล้ว "หน้าจอมืดสนิท ไม่มีอะไรขึ้นมาเลย" ทั้งที่ข้อมูลมีอยู่จริง สาเหตุส่วนใหญ่เกิดจากความสับสนระหว่างการย่อฟังก์ชันลูกศร (Arrow Function Return):

❌ แบบที่ผิด: ใส่ปีกกา { } แต่ลืมพิมพ์ returnitems.map(item => { <Card data={item} /> // 💥 พัง! จะไม่มีอะไรแสดงบนจอเลยเพราะไม่มีการส่งค่าออก })
✔️ แบบที่ 1 (แนะนำ): เขียนย่อโดยใช้วงเล็บโค้ง ( ) แทนปีกกาitems.map(item => ( <Card data={item} /> // ✨ ทำงานปกติ เพราะวงเล็บโค้งจะคายค่าให้อัตโนมัติ (Implicit Return) ))
✔️ แบบที่ 2: ใช้ปีกกา { } และระบุ return ให้แจ่มชัดitems.map(item => { // สไตล์นี้เหมาะกับการประกาศตัวแปรคำนวณด้านบนก่อนคืนค่า const finalPrice = item.price * 0.9; // ส่วนลด 10% return <Card data={item} price={finalPrice} />; // ✨ ทำงานปกติ })

3.filter() — ประตูคัดเลือกของที่ใช่ และปุ่ม "ลบรายการ"

หากเปรียบ .map() คือโรงงานปั๊มชิ้นงาน .filter() ก็คือ "ยามเฝ้าประตูที่เข้มงวด" (The Strict Security Guard)

มันจะวิ่งตรวจสมาชิกในอาเรย์ทีละชิ้น แล้วส่งค่าแต่ละชิ้นเข้าไปเช็คในเงื่อนไขที่เราตั้งไว้ ซึ่งผลการตรวจจะมีแค่ 2 อย่างเท่านั้น:

  • สอบผ่าน (คายค่าเป็น true): อนุญาตให้เดินผ่านเข้ารอบไปอยู่ในอาร์เรย์ใหม่ปลายทาง
  • สอบตก (คายค่าเป็น false): ถูกถอดชื่อทิ้งทันที ไม่มีสิทธิ์ได้ไปต่อบนหน้าจอ
ภาพการจำลองการทำงานของ .filter()
[🍩 49฿, 🍰 85฿, 🥐 65฿]
.filter( item => item.price < 60 )
[🍩 49฿]

ในโลกการพัฒนาเว็บแอปพลิเคชันจริง เราใช้ .filter() บ่อยที่สุดใน 2 สถานการณ์ดังนี้:

Aการค้นหาและการคัดกรองสินค้า (Search & Category Filtering)

คัดเลือกเฉพาะเมนูที่ผู้ใช้คลิกเลือกหมวดหมู่ หรือพิมพ์คำค้นหาในช่องค้นหา:

// คัดเฉพาะรายการสินค้าที่มีชื่อคำว่า "เค้ก"
const cakeOnly = items.filter(item => item.name.includes("เค้ก"));
Bการทำระบบลบรายการ (Delete Action)

นี่คือแนวคิดที่ยอดเยี่ยมมาก! เวลาเราต้องการลบชิ้นใดชิ้นหนึ่งออก แทนที่เราจะเดินไปลบทิ้งเฉยๆ เราจะใช้วิธี "กรองเก็บทุกตัวยกเว้นไอดีตัวที่เราจะลบ" แทน:

// ลบรายการที่มี ID เป็น "5" ออกจากจอ
// ผลลัพธ์: จะได้ชุดข้อมูลใหม่ที่มีข้อมูลครบทุกชิ้นยกเว้น ID: "5"
const updatedItems = items.filter(item => item.id !== "5");

โรงงานเบเกอรี่จำลอง (Map & Filter Bakery Sandbox)

ลองเล่นในส่วนของร้านเบเกอรี่ด้านล่างได้เลย! ผู้เรียนสามารถคลิกเลือก **Filter** ในแบบต่างๆ หรือเลือกเปลี่ยน **Map Style** และกดปุ่มไอคอน **ถังขยะ** เพื่อลบรายการสินค้า หรือพิมพ์ข้อมูลเพิ่มขนมหวานชนิดใหม่เข้าไป แล้วสังเกตการเปลี่ยนแปลงโค้ดและจำนวนสมาชิกใน Array จริงที่ตอบสนองอยู่ด้านซ้ายมือได้เลย!

Filter:
Map Style:

การกรองและปั๊ม Component (React Code)

// 1. คัดกรองข้อมูลด้วย .filter()
const filtered = items.filter(item => {
  return true; // แสดงทั้งหมด
});

// 2. แปลงร่างข้อมูลเป็น UI ด้วย .map()
return (
  <div className="grid">
    {filtered.map(item => (
      <BakeryCard
        key={item.id} // สำคัญมาก!
        data={item}
        onDelete={() => handleDelete(item.id)}
      />
    ))}
  </div>
);
เพิ่มสินค้าเข้า Array ต้นทาง (Immutable Push)

การกดปุ่มนี้จะไปรัน setItems([...items, newItem]) เพื่อถนอมชุดข้อมูลเดิม และเพิ่มข้อมูลชิ้นใหม่ลงไปในอาเรย์

ข้อมูลผลลัพธ์ (Rendered Output)
พบ 5 รายการ
🍩 สตรอว์เบอร์รีโดนัท
ID: 1 | TYPE: donut
ราคาจำหน่าย49 บาท
🍰 นิวยอร์กชีสเค้ก
ID: 2 | TYPE: cake
ราคาจำหน่าย85 บาท
🥐 ครัวซองต์ฝรั่งเศส
ID: 3 | TYPE: bread
ราคาจำหน่าย65 บาท
🧁 บลูเบอร์รีมัฟฟิน
ID: 4 | TYPE: cake
ราคาจำหน่าย55 บาท
🥖 บาแก็ตเนยกระเทียม
ID: 5 | TYPE: bread
ราคาจำหน่าย40 บาท

ทำไม React บังคับให้เขียน key={item.id}?

เวลาเราสั่ง **ลบข้อมูล** (กดปุ่มไอคอนถังขยะ) โค้ดจะใช้ `.filter()` เพื่อลบรายการนั้นออกจากอาเรย์ และ React จะใช้ `key` ในการสั่งการเรนเดอร์เฉพาะการ์ดชิ้นนั้นออกจากหน้าจอได้อย่างถูกต้อง หากไม่มี `key` หรือใช้ดัชนี (`index`) แบบไม่เฉพาะเจาะจง หน้าจออาจจะเรนเดอร์ช้าลง และส่งผลให้แอปแสดงผลผิดพลาดได้

.map() เอาไว้ใช้ปั๊มข้อมูลออกมาโชว์ และ .filter() เอาไว้ใช้คัดแยกของหรือลบรายการออกจากหน้าจอ

4กฎเหล็ก: ทำไมต้องเขียน key={item.id} เสมอ? 🔑

เวลาเราเขียนโค้ดใน React เพื่อเอาข้อมูลมาวนแสดงผลด้วย `.map()` สิ่งหนึ่งที่เบราว์เซอร์จะประท้วงแจ้งเตือนสีแดงบ่อยที่สุดหากละเลยคือข้อความบ่นเรื่อง "Each child in a list should have a unique 'key' prop."

ความลับของระบบ Virtual DOM Reconciliation (การเปรียบเทียบความแตกต่าง)

เมื่ออาเรย์ใน State มีการอัปเดต React จะไม่ได้ล้างหน้าจอและวาดใหม่ทั้งหมด แต่ใช้ระบบที่ฉลาดมากเรียกว่า **Virtual DOM** ในการตรวจสอบชิ้นต่อชิ้นว่า "จุดไหนบนจอที่เปลี่ยนไปจริง จะได้สั่งแก้ไขเฉพาะตำแหน่งนั้นเพื่อความลื่นไหล"

หากไม่มีการระบุ key ให้กับการ์ดแต่ละใบ เมื่อเกิดการสลับลำดับ สลับตำแหน่ง หรือถูกลบทิ้ง React จะแยกแยะไม่ออกเลยว่ากล่องไหนคือกล่องไหน และจำใจต้องเคลียร์วาดใหม่ยกแผง ทำให้ประสิทธิภาพการทำงานตกลงอย่างน่าใจหาย

ประเภทของ Keyตัวอย่างโค้ดผลลัพธ์การทำงาน / บั๊กที่อาจพบ
✅ ใช้ ID จริงไม่ซ้ำกัน (Best)key={item.id}ยอดเยี่ยมที่สุด! ข้อมูล ID คงตัวไม่ว่าลำดับจะเปลี่ยนไปอย่างไร แอนิเมชันลื่นไหล ทำงานเสถียรที่สุด
⚠️ ใช้ Index ของอาร์เรย์ (Avoid)key={index}พอใช้ได้ถ้าข้อมูลอยู่นิ่งๆ **แต่ห้ามใช้เด็ดขาด** หากมีการลบหรือเพิ่มข้อมูล เพราะดัชนีลำดับจะสลับตำแหน่งไปเกาะตัวอื่น ทำให้ค่าอินพุตเพี้ยนและภาพสับสน
❌ ใช้คำสั่งสุ่มค่า (Worst)key={Math.random()}เลวร้ายที่สุด! React จะประเมินว่าการ์ดชิ้นนั้นคือชิ้นใหม่แกะกล่องทุกเสี้ยววินาที สั่งลบวาดใหม่ตลอดเวลา ทำให้พิมพ์ช่องอินพุตไม่ได้และเว็บกระตุกอย่างรุนแรง

สรุปสามัญประจำใจสำหรับ Key:

  • ✔️ต้องไม่ซ้ำกัน (Unique): key ของการ์ดใบข้างๆ ต้องไม่เหมือนกัน
  • ✔️ต้องคงตัว (Stable): ไม่เปลี่ยนแปลงค่าไปมาในการเรนเดอร์รอบหน้า

5การอัปเดต Array ใน State อย่างปลอดภัย (Immutability) 🛡️

อีกหนึ่งก้าวที่ท้าทายที่สุดของโปรแกรมเมอร์ React คือการทำความเข้าใจเรื่อง Immutability (ความไม่กลายพันธุ์) ของชุดข้อมูลใน State

ในภาษาจาวาสคริปต์ทั่วไป เวลาเราต้องการเพิ่มข้อมูลลงอาเรย์ เรามักจะนึกถึงคำสั่ง array.push(newItem) ใช่หรือไม่?

แต่ใน React เรามีกฎเหล็กว่า ห้ามเข้าไปดัดแปลงข้อมูลดิบในสมอง (State) โดยตรงเด็ดขาด!

ทำไม React ถึงใจร้ายห้ามแก้ไข Array ตรงๆ? (Reference Comparison)

ในภาษาคอมพิวเตอร์ ข้อมูลชนิดออบเจกต์และอาร์เรย์จะไม่ได้เก็บข้อมูลดิบไว้ในตัวแปลเฉยๆ แต่จะเก็บ "ที่อยู่หน่วยความจำ" (Memory Reference Pointer) เอาไว้ชี้เป้า

เวลา React ตรวจจับว่าต้องเรนเดอร์จอใหม่ไหม มันจะใช้วิธีเปรียบเทียบที่อยู่ oldState === newStateถ้าเราสั่ง items.push(newItem) ข้อมูลดิบในอาร์เรย์เพิ่มขึ้นจริง แต่ที่อยู่บนแรมยังคงเป็นตำแหน่งเดิมเป๊ะ! React ก็จะวิเคราะห์ว่า "อ้าว ที่อยู่เดิมนี่นา แสดงว่าไม่มีการเปลี่ยนแปลงอะไร" ผลก็คือหน้าจอเฉยเมยไม่ยอมขยับตาม

ดังนั้น วิธีที่ถูกต้องคือเราต้อง "กางก๊อปปี้ชิ้นงานเก่ามาแปะรวมกับชิ้นใหม่ในที่อยู่ใหม่" (สร้างอาร์เรย์ใหม่เอี่ยม) เสมอ:

❌ วิธีลักไก่แบบผิดๆ (Mutating State)
// 💥 1. เพิ่มข้อมูลตรงๆ
items.push(newItem); 
setItems(items); // จอนิ่งสนิท!

// 💥 2. ลบข้อมูลตรงๆ ด้วย splice
items.splice(index, 1);
setItems(items); // จอก็นิ่งสนิท!
✔️ วิธีอัปเดตแบบไร้ที่ติ (Immutable update)
// ✨ 1. เพิ่มข้อมูลด้วย Spread Operator [...]
setItems([...items, newItem]); 

// ✨ 2. ลบข้อมูลด้วยฟังก์ชัน .filter()
// (เพราะ filter คืนค่าเป็นอาร์เรย์ชุดใหม่ทันที!)
setItems(items.filter(item => item.id !== targetId));

ตารางเทียบคำสั่งการจัดการ Array ใน React:

วัตถุประสงค์คำสั่งห้ามใช้ (Mutate)คำสั่งที่ถูกต้องสไตล์ React
เพิ่มต่อท้าย (Push).push()[...arr, newItem]
เพิ่มนำหน้า (Unshift).unshift()[newItem, ...arr]
ลบสมาชิก (Remove).splice(), .pop().filter(item => ...)
แก้ไขข้อมูลด้านใน (Edit)arr[i] = newValue.map(item => ...)

💡 เจาะลึกระดับผู้เริ่มต้น: การสร้างระบบเพิ่มข้อมูลใหม่เข้าสู่รายการ (Step-by-Step Data Addition Flow)

เมื่อเราเรียนรู้เรื่อง "ความไม่กลายพันธุ์ (Immutability)" ของอาร์เรย์แล้ว หลายคนอาจสงสัยว่า"แล้วในชีวิตจริง เราจะเขียนโค้ดช่องกรอกข้อมูลร่วมกับปุ่มกด เพื่อพิมพ์เพิ่มรายการลงบนหน้าเว็บจริงๆ ได้อย่างไร?"หัวข้อนี้จะจับมือผู้เรียนสร้างระบบเพิ่มขนมหวาน (Bakery Item Adder) ตั้งแต่นับหนึ่งแบบเข้าใจง่ายที่สุดค่ะ

1

ขั้นตอนที่ 1: เตรียมสมองสองส่วน (The Two-State Setup)

ก่อนอื่นเราต้องสร้างกล่องจดจำไว้สองกล่อง กล่องแรกใช้เก็บ "รายการสินค้าทั้งหมดที่มี" (Array State)และกล่องที่สองใช้เก็บ "ตัวหนังสือที่ผู้ใช้กำลังพิมพ์ค้างไว้ชั่วคราว" (Input Text State):

// 1. กล่องเก็บของทั้งหมดในร้าน (Array State)
const [items, setItems] = useState([
  { id: "1", name: "🍩 โดนัท" }
]);

// 2. กล่องเก็บคำที่กำลังพิมพ์อยู่คาช่องกรอก (Input State)
const [newName, setNewName] = useState("");
2

ขั้นตอนที่ 2: เชื่อมช่องกรอกให้ดักฟังเสียงคีย์บอร์ด (onChange Input Binding)

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

<input 
  type="text" 
  value={newName} // 👈 บังคับดึงคำจากกล่องพิมพ์ชั่วคราวมาแสดง
  onChange={(e) => setNewName(e.target.value)} // 👈 ทุกครั้งที่พิมพ์ ให้ดึงคำใหม่ไปเก็บในกล่องพิมพ์ชั่วคราว
  placeholder="พิมพ์ชื่อขนมใหม่..."
/>
3

ขั้นตอนที่ 3: เขียนฟังก์ชันปรุงข้อมูลชิ้นใหม่แล้วก๊อปปี้เพิ่ม (The Array Adder Logic)

นี่คือส่วนสำคัญที่สุด! เมื่อผู้ใช้กดปุ่มเพิ่ม เราจะปรุงไอเทมชิ้นใหม่โดยนำคำพิมพ์ค้างชั่วคราวมาประกอบไอดี แล้วอัปเดตรายการสินค้าด้วยสเปรดโอเปอเรเตอร์ [...items, newItem] จากนั้นจึง ล้างคำพิมพ์ทิ้ง ให้ช่องกรอกว่างเปล่า:

const handleAddItem = () => {
  // ดักฟัง: ถ้าเว้นช่องว่างเปล่า ไม่ต้องยอมให้แอดเข้า
  if (newName.trim() === "") return;

  // ปรุงวัตถุดิบชิ้นใหม่
  const newItem = {
    id: Date.now().toString(), // สุ่มไอดีไม่ซ้ำกันด้วยเลขเวลาวินาที
    name: newName              // คำที่พิมพ์อยู่คาช่องกรอก
  };

  // ✅ สั่งก๊อปปี้รายการเดิมทั้งหมด มารวมกับชิ้นใหม่ในที่อยู่แรมใหม่เอี่ยม!
  setItems([...items, newItem]);

  // ✅ ล้างช่องกรอกข้อความให้กลับมาเป็นสตริงว่าง "" เคลียร์พื้นที่รอพิมพ์ครั้งต่อไป
  setNewName("");
};
4

ขั้นตอนที่ 4: สั่งลูปสแตมป์ชิ้นงานออกสู่สายตา (Rendering Output with key)

ขั้นตอนสุดท้ายคือนำรายการสินค้าทั้งหมด (items) ไปวิ่งผ่านสายพาน .map() เพื่อพ่นหน้าตาแถวรายการออกมาทีละชิ้นพร้อมผูกกุญแจ key:

<ul>
  {items.map((item) => (
    <li key={item.id} className="py-2 border-b">
      {item.name}
    </li>
  ))}
</ul>

🧠 โมเดลจำลองความคิด: การเพิ่มข้อมูลสไตล์ React

ลองเปรียบเทียบกับชีวิตจริง: การใช้คำสั่ง items.push(newItem) คือการเขียนชื่อสมาชิกเพิ่มลงบนกระดาษใบเก่า แผ่นกระดานของ React จะตรวจเพียงแค่กระดาษชิ้นเดิมหรือไม่ จึงไม่รู้ว่ากระดาษแผ่นเก่ามีการเปลี่ยนแปลงอะไร

แต่สำหรับวิธีการ setItems([...items, newItem]) คือการหยิบเครื่องถ่ายเอกสารมาสแกนรายชื่อเก่าทั้งหมดบนกระดาษแผ่นเก่า แล้วปริ้นท์พ่นข้อความลงบนกระดาษแผ่นใหม่เอี่ยมพร้อมกับพิมพ์รายชื่อใหม่เพิ่มต่อท้ายเข้าไป จากนั้นยื่นกระดาษแผ่นใหม่ส่งให้ React เมื่อ React มองเห็นกระดาษแผ่นใหม่ชิ้นอื่น ก็จะเข้าใจทันทีว่าข้อมูลเปลี่ยนไป และเริ่มวาดภาพหน้าจอใหม่ให้ตรงกันอย่างสมบูรณ์แบบค่ะ!


บททดสอบท้ายบทเรียน (Quiz)

มาวัดพลังความเข้าใจเรื่อง Map, Filter และการควบคุม Array กันดีกว่า!

🧠 ทบทวนความเข้าใจบทที่ 7 (Mini Quiz)

ข้อ 1 จาก 5

Q:เหตุใด React จึงบังคับให้ระบุ Prop พิเศษชื่อ "key" ให้กับแท็กนอกสุดเสมอเวลาเราทำการวนลูปแสดงผลด้วย .map()?

💡

ลองศึกษาเฉลยเพื่อดูแนวคิดเพิ่มเติม...

ถูกต้องที่สุด key คือตัวช่วยของ React Engine หากไม่มี key เวลาอาร์เรย์เปลี่ยนลำดับหรือถูกลบออก React จะต้องลบและวาดหน้าจอใหม่ทั้งหมดแทนที่จะลบหรืออัปเดตเฉพาะชิ้นเดี่ยว ซึ่งส่งผลต่อความเร็วและทำให้เกิดข้อผิดพลาดได้

คุณเรียนจบบทที่ 7 แล้ว!

บทต่อไป: การแสดงผลแบบมีเงื่อนไข (Conditional Rendering)

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

เรียนบทต่อไป