ทำความเข้าใจกระบวนการ reconciliation ของ React เรียนรู้วิธีใช้ 'key' prop เพื่อเพิ่มประสิทธิภาพการแสดงผลลิสต์ ป้องกันบั๊ก และเร่งความเร็วแอปพลิเคชัน คู่มือสำหรับนักพัฒนา
ปลดล็อกประสิทธิภาพ: เจาะลึกเรื่อง Key ในกระบวนการ Reconciliation ของ React เพื่อการเพิ่มประสิทธิภาพการแสดงผลลิสต์
ในโลกของการพัฒนาเว็บสมัยใหม่ การสร้างส่วนติดต่อผู้ใช้ (UI) ที่ตอบสนองต่อการเปลี่ยนแปลงข้อมูลได้อย่างรวดเร็วถือเป็นสิ่งสำคัญสูงสุด React ซึ่งมีสถาปัตยกรรมแบบคอมโพเนนต์และลักษณะการทำงานแบบ declarative ได้กลายเป็นมาตรฐานระดับโลกสำหรับการสร้าง UI เหล่านี้ หัวใจสำคัญของประสิทธิภาพของ React คือกระบวนการที่เรียกว่า reconciliation ซึ่งเกี่ยวข้องกับ Virtual DOM อย่างไรก็ตาม แม้แต่เครื่องมือที่ทรงพลังที่สุดก็อาจถูกใช้งานอย่างไม่มีประสิทธิภาพได้ และจุดที่นักพัฒนาทั้งมือใหม่และผู้มีประสบการณ์มักจะพลาดคือการแสดงผลลิสต์ (rendering of lists)
คุณคงเคยเขียนโค้ดอย่าง data.map(item => <div>{item.name}</div>)
มานับครั้งไม่ถ้วน มันดูเรียบง่ายจนแทบไม่มีอะไร แต่ภายใต้ความเรียบง่ายนี้กลับซ่อนข้อควรพิจารณาด้านประสิทธิภาพที่สำคัญไว้ ซึ่งหากถูกละเลย อาจนำไปสู่แอปพลิเคชันที่เชื่องช้าและบั๊กที่น่าสับสน ทางแก้คืออะไรน่ะหรือ? prop เล็กๆ แต่ทรงพลังที่ชื่อว่า key
นั่นเอง
คู่มือฉบับสมบูรณ์นี้จะพาคุณเจาะลึกกระบวนการ reconciliation ของ React และบทบาทที่ขาดไม่ได้ของ key ในการแสดงผลลิสต์ เราจะสำรวจไม่ใช่แค่ 'อะไร' แต่คือ 'ทำไม'—ทำไม key ถึงจำเป็น, วิธีเลือก key ที่ถูกต้อง, และผลกระทบที่ร้ายแรงเมื่อเลือกผิด เมื่ออ่านจบ คุณจะมีความรู้ในการเขียนแอปพลิเคชัน React ที่มีประสิทธิภาพ เสถียร และเป็นมืออาชีพมากขึ้น
บทที่ 1: ทำความเข้าใจ Reconciliation ของ React และ Virtual DOM
ก่อนที่เราจะเข้าใจความสำคัญของ key เราต้องทำความเข้าใจกลไกพื้นฐานที่ทำให้ React ทำงานได้รวดเร็วก่อน นั่นคือ reconciliation ซึ่งขับเคลื่อนโดย Virtual DOM (VDOM)
Virtual DOM คืออะไร?
การโต้ตอบกับ Document Object Model (DOM) ของเบราว์เซอร์โดยตรงนั้นใช้ทรัพยากรในการคำนวณสูง ทุกครั้งที่คุณเปลี่ยนแปลงบางอย่างใน DOM—เช่น การเพิ่มโหนด, อัปเดตข้อความ, หรือเปลี่ยนสไตล์—เบราว์เซอร์ต้องทำงานหนักอย่างมาก มันอาจจะต้องคำนวณสไตล์และเลย์เอาต์ใหม่สำหรับทั้งหน้า ซึ่งเป็นกระบวนการที่เรียกว่า reflow และ repaint ในแอปพลิเคชันที่ซับซ้อนและขับเคลื่อนด้วยข้อมูล การแก้ไข DOM โดยตรงบ่อยครั้งสามารถทำให้ประสิทธิภาพลดลงอย่างรวดเร็ว
React ได้นำเสนอชั้นของ abstraction (abstraction layer) เพื่อแก้ปัญหานี้ นั่นคือ Virtual DOM VDOM คือการจำลอง DOM จริงที่มีน้ำหนักเบาและอยู่ในหน่วยความจำ ลองนึกว่ามันเป็นพิมพ์เขียวของ UI ของคุณ เมื่อคุณสั่งให้ React อัปเดต UI (เช่น โดยการเปลี่ยน state ของคอมโพเนนต์) React จะไม่ไปยุ่งกับ DOM จริงในทันที แต่จะทำตามขั้นตอนต่อไปนี้:
- สร้าง VDOM tree ใหม่ที่แสดงถึง state ที่อัปเดตแล้ว
- เปรียบเทียบ VDOM tree ใหม่นี้กับ VDOM tree ก่อนหน้า กระบวนการเปรียบเทียบนี้เรียกว่า "diffing"
- React จะคำนวณหาชุดการเปลี่ยนแปลงที่น้อยที่สุดที่จำเป็นในการเปลี่ยน VDOM เก่าให้เป็น VDOM ใหม่
- การเปลี่ยนแปลงที่น้อยที่สุดเหล่านี้จะถูกรวบรวมและนำไปใช้กับ DOM จริงในครั้งเดียวอย่างมีประสิทธิภาพ
กระบวนการนี้ที่เรียกว่า reconciliation คือสิ่งที่ทำให้ React มีประสิทธิภาพสูง แทนที่จะสร้างบ้านใหม่ทั้งหลัง React ทำตัวเหมือนผู้รับเหมาผู้เชี่ยวชาญที่ระบุได้อย่างแม่นยำว่าต้องเปลี่ยนอิฐก้อนไหนบ้าง ซึ่งช่วยลดงานและการหยุดชะงักให้น้อยที่สุด
บทที่ 2: ปัญหาของการแสดงผลลิสต์โดยไม่มี Key
ตอนนี้ เรามาดูกันว่าระบบที่สวยงามนี้จะเจอปัญหาได้อย่างไร ลองพิจารณาคอมโพเนนต์ง่ายๆ ที่แสดงผลรายชื่อผู้ใช้:
function UserList({ users }) {
return (
<ul>
{users.map(user => (
<li>{user.name}</li>
))}
</ul>
);
}
เมื่อคอมโพเนนต์นี้แสดงผลครั้งแรก React จะสร้าง VDOM tree ขึ้นมา ถ้าเราเพิ่มผู้ใช้ใหม่ไปที่ *ท้าย* ของอาร์เรย์ `users` อัลกอริทึม diffing ของ React จะจัดการได้อย่างราบรื่น มันจะเปรียบเทียบลิสต์เก่ากับลิสต์ใหม่ เห็นว่ามีรายการใหม่ที่ส่วนท้าย และเพียงแค่เพิ่ม `<li>` ใหม่เข้าไปใน DOM จริง ซึ่งมีประสิทธิภาพและเรียบง่าย
แต่จะเกิดอะไรขึ้นถ้าเราเพิ่มผู้ใช้ใหม่เข้าไปที่ *ตอนต้น* ของลิสต์ หรือจัดลำดับรายการใหม่?
สมมติว่าลิสต์เริ่มต้นของเราคือ:
- Alice
- Bob
และหลังจากการอัปเดต กลายเป็น:
- Charlie
- Alice
- Bob
เมื่อไม่มีตัวระบุที่ไม่ซ้ำกัน React จะเปรียบเทียบลิสต์ทั้งสองตามลำดับ (index) นี่คือสิ่งที่มันเห็น:
- ตำแหน่งที่ 0: รายการเก่าคือ "Alice" รายการใหม่คือ "Charlie" React สรุปว่าคอมโพเนนต์ที่ตำแหน่งนี้ต้องได้รับการอัปเดต มันจึงแก้ไข (mutate) DOM node ที่มีอยู่เพื่อเปลี่ยนเนื้อหาจาก "Alice" เป็น "Charlie"
- ตำแหน่งที่ 1: รายการเก่าคือ "Bob" รายการใหม่คือ "Alice" React แก้ไข DOM node ที่สองเพื่อเปลี่ยนเนื้อหาจาก "Bob" เป็น "Alice"
- ตำแหน่งที่ 2: ก่อนหน้านี้ไม่มีรายการใดๆ ที่นี่ รายการใหม่คือ "Bob" React สร้างและแทรก DOM node ใหม่สำหรับ "Bob"
นี่เป็นกระบวนการที่ไม่มีประสิทธิภาพอย่างยิ่ง แทนที่จะแค่แทรกอิลิเมนต์ใหม่สำหรับ "Charlie" เพียงตัวเดียวที่จุดเริ่มต้น React กลับทำการแก้ไข (mutation) สองครั้งและแทรก (insertion) อีกหนึ่งครั้ง สำหรับลิสต์ขนาดใหญ่ หรือสำหรับรายการที่เป็นคอมโพเนนต์ซับซ้อนและมี state เป็นของตัวเอง การทำงานที่ไม่จำเป็นนี้จะนำไปสู่การลดลงของประสิทธิภาพอย่างมาก และที่สำคัญกว่านั้นคืออาจเกิดบั๊กเกี่ยวกับ state ของคอมโพเนนต์ได้
นี่คือเหตุผลว่าทำไมถ้าคุณรันโค้ดข้างต้น developer console ของเบราว์เซอร์จะแสดงคำเตือนว่า: "Warning: Each child in a list should have a unique 'key' prop." React กำลังบอกคุณอย่างชัดเจนว่ามันต้องการความช่วยเหลือเพื่อทำงานได้อย่างมีประสิทธิภาพ
บทที่ 3: `key` Prop ผู้ช่วยชีวิต
key
prop คือคำใบ้ที่ React ต้องการ มันเป็น attribute พิเศษประเภท string ที่คุณต้องระบุเมื่อสร้างลิสต์ของอิลิเมนต์ Key จะทำให้อิลิเมนต์แต่ละตัวมีตัวตนที่เสถียรและไม่ซ้ำกันในทุกๆ การ re-render
มาเขียนคอมโพเนนต์ `UserList` ของเราใหม่โดยใช้ key กัน:
function UserList({ users }) {
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
ในที่นี้ เราสมมติว่าอ็อบเจกต์ `user` แต่ละตัวมี property `id` ที่ไม่ซ้ำกัน (เช่น มาจากฐานข้อมูล) ตอนนี้ ลองกลับไปดูสถานการณ์ของเราอีกครั้ง
ข้อมูลเริ่มต้น:
[{ id: 'u1', name: 'Alice' }, { id: 'u2', name: 'Bob' }]
ข้อมูลที่อัปเดต:
[{ id: 'u3', name: 'Charlie' }, { id: 'u1', name: 'Alice' }, { id: 'u2', name: 'Bob' }]
เมื่อมี key แล้ว กระบวนการ diffing ของ React จะฉลาดขึ้นมาก:
- React จะดู children ของ `<ul>` ใน VDOM ใหม่และตรวจสอบ key ของพวกมัน มันจะเห็น `u3`, `u1`, และ `u2`
- จากนั้นมันจะตรวจสอบ children ของ VDOM ก่อนหน้าและ key ของพวกมัน มันจะเห็น `u1` และ `u2`
- React รู้ว่าคอมโพเนนต์ที่มี key `u1` และ `u2` มีอยู่แล้ว มันไม่จำเป็นต้องแก้ไข แต่แค่ย้าย DOM node ที่สอดคล้องกันไปยังตำแหน่งใหม่
- React เห็นว่า key `u3` เป็นของใหม่ มันจึงสร้างคอมโพเนนต์และ DOM node ใหม่สำหรับ "Charlie" และแทรกไว้ที่จุดเริ่มต้น
ผลลัพธ์คือมีการแทรก DOM เพียงครั้งเดียวและการจัดลำดับใหม่บางส่วน ซึ่งมีประสิทธิภาพมากกว่าการแก้ไขหลายครั้งและการแทรกที่เราเห็นก่อนหน้านี้ Key ได้ให้ตัวตนที่เสถียร ทำให้ React สามารถติดตามอิลิเมนต์ต่างๆ ตลอดการ re-render โดยไม่คำนึงถึงตำแหน่งของมันในอาร์เรย์
บทที่ 4: การเลือก Key ที่ถูกต้อง - กฎทอง
ประสิทธิภาพของ `key` prop ขึ้นอยู่กับการเลือกค่าที่ถูกต้องทั้งหมด มีแนวทางปฏิบัติที่ดีที่สุดที่ชัดเจนและ anti-pattern ที่อันตรายที่ควรระวัง
Key ที่ดีที่สุด: ID ที่ไม่ซ้ำกันและเสถียร
key ในอุดมคติคือค่าที่ระบุรายการในลิสต์ได้อย่างไม่ซ้ำกันและถาวร ซึ่งโดยส่วนใหญ่มักจะเป็น ID ที่ไม่ซ้ำกันจากแหล่งข้อมูลของคุณ
- ต้องไม่ซ้ำกันในหมู่พี่น้อง (siblings) Key ไม่จำเป็นต้องไม่ซ้ำกันทั่วทั้งแอปพลิเคชัน ขอเพียงแค่ไม่ซ้ำกันภายในลิสต์ของอิลิเมนต์ที่กำลังแสดงผลในระดับเดียวกัน ลิสต์สองรายการที่แตกต่างกันในหน้าเดียวกันสามารถมีรายการที่มี key เหมือนกันได้
- ต้องมีความเสถียร Key สำหรับรายการข้อมูลหนึ่งๆ ไม่ควรเปลี่ยนแปลงระหว่างการ re-render หากคุณดึงข้อมูลของ Alice มาใหม่ เธอก็ควรจะยังมี `id` เดิม
แหล่งที่มาของ key ที่ยอดเยี่ยม ได้แก่:
- Primary key จากฐานข้อมูล (เช่น `user.id`, `product.sku`)
- Universally Unique Identifiers (UUIDs)
- สตริงที่ไม่ซ้ำกันและไม่เปลี่ยนแปลงจากข้อมูลของคุณ (เช่น ISBN ของหนังสือ)
// ดี: ใช้ ID ที่เสถียรและไม่ซ้ำกันจากข้อมูล
<div>
{products.map(product => (
<ProductItem key={product.sku} product={product} />
))}
</div>
Anti-Pattern: การใช้ Array Index เป็น Key
ข้อผิดพลาดที่พบบ่อยคือการใช้ index ของอาร์เรย์เป็น key:
// แย่: การใช้ index ของอาร์เรย์เป็น key
<div>
{items.map((item, index) => (
<ListItem key={index} item={item} />
))}
</div>
แม้ว่าวิธีนี้จะทำให้คำเตือนของ React หายไป แต่มันอาจนำไปสู่ปัญหาร้ายแรงและโดยทั่วไปถือว่าเป็น anti-pattern การใช้ index เป็น key เป็นการบอก React ว่าตัวตนของรายการนั้นผูกอยู่กับตำแหน่งของมันในลิสต์ ซึ่งเป็นปัญหาพื้นฐานเดียวกับการไม่มี key เลยเมื่อลิสต์สามารถจัดลำดับใหม่, กรอง, หรือมีรายการเพิ่ม/ลบออกจากตอนต้นหรือตอนกลางได้
บั๊กการจัดการ State:
ผลข้างเคียงที่อันตรายที่สุดของการใช้ index key จะปรากฏขึ้นเมื่อรายการในลิสต์ของคุณจัดการ state ของตัวเอง ลองนึกภาพลิสต์ของ input field:
function UnstableList() {
const [items, setItems] = React.useState([{ id: 1, text: 'First' }, { id: 2, text: 'Second' }]);
const handleAddItemToTop = () => {
setItems([{ id: 3, text: 'New Top' }, ...items]);
};
return (
<div>
<button onClick={handleAddItemToTop}>Add to Top</button>
{items.map((item, index) => (
<div key={index}>
<label>{item.text}: </label>
<input type="text" />
</div>
))}
</div>
);
}
ลองนึกภาพตามนี้:
- ลิสต์แสดงผลด้วย "First" และ "Second"
- คุณพิมพ์ "Hello" ลงใน input field แรก (อันที่เป็นของ "First")
- คุณคลิกปุ่ม "Add to Top"
คุณคาดหวังว่าจะเกิดอะไรขึ้น? คุณคงคาดหวังว่า input ว่างๆ อันใหม่สำหรับ "New Top" จะปรากฏขึ้น และ input สำหรับ "First" (ที่ยังคงมีคำว่า "Hello" อยู่) จะเลื่อนลงไป แต่สิ่งที่เกิดขึ้นจริงคืออะไร? input field ที่ตำแหน่งแรก (index 0) ซึ่งยังคงมี "Hello" อยู่ จะยังคงอยู่ แต่ตอนนี้มันเชื่อมโยงกับรายการข้อมูลใหม่คือ "New Top" state ของคอมโพเนนต์ input (ค่าภายในของมัน) ถูกผูกติดอยู่กับตำแหน่งของมัน (key=0) ไม่ใช่ข้อมูลที่มันควรจะแสดง นี่คือบั๊กคลาสสิกที่น่าสับสนซึ่งเกิดจากการใช้ index key
หากคุณเพียงแค่เปลี่ยน `key={index}` เป็น `key={item.id}` ปัญหาก็จะหมดไป React จะสามารถเชื่อมโยง state ของคอมโพเนนต์กับ ID ที่เสถียรของข้อมูลได้อย่างถูกต้อง
เมื่อไหร่ถึงจะยอมรับการใช้ Index Key ได้?
มีบางสถานการณ์ที่หายากซึ่งการใช้ index นั้นปลอดภัย แต่คุณต้องปฏิบัติตามเงื่อนไขทั้งหมดนี้:
- ลิสต์เป็นแบบ static: จะไม่มีการจัดลำดับใหม่, กรอง, หรือมีรายการเพิ่ม/ลบจากที่ใดๆ ยกเว้นตอนท้าย
- รายการในลิสต์ไม่มี ID ที่เสถียร
- คอมโพเนนต์ที่แสดงผลสำหรับแต่ละรายการนั้นเรียบง่ายและไม่มี state ภายใน
ถึงกระนั้น ก็ยังดีกว่าที่จะสร้าง ID ชั่วคราวแต่เสถียรขึ้นมาถ้าเป็นไปได้ การใช้ index ควรเป็นการตัดสินใจอย่างรอบคอบเสมอ ไม่ใช่ค่าเริ่มต้น
ผู้ร้ายตัวฉกาจที่สุด: `Math.random()`
อย่าใช้ `Math.random()` หรือค่าที่ไม่สามารถคาดเดาได้ (non-deterministic) อื่นๆ มาเป็น key เด็ดขาด:
// แย่มาก: อย่าทำแบบนี้เด็ดขาด!
<div>
{items.map(item => (
<ListItem key={Math.random()} item={item} />
))}
</div>
key ที่สร้างจาก `Math.random()` จะได้ค่าที่แตกต่างกันทุกครั้งที่ re-render ซึ่งเป็นการบอก React ว่าลิสต์ของคอมโพเนนต์ทั้งหมดจากการ render ครั้งก่อนได้ถูกทำลายไปแล้ว และมีการสร้างลิสต์ใหม่ของคอมโพเนนต์ที่แตกต่างกันโดยสิ้นเชิงขึ้นมาแทน สิ่งนี้บังคับให้ React ต้อง unmount คอมโพเนนต์เก่าทั้งหมด (ทำลาย state ของมัน) และ mount คอมโพเนนต์ใหม่ทั้งหมด ซึ่งเป็นการทำลายจุดประสงค์ของ reconciliation โดยสิ้นเชิง และเป็นตัวเลือกที่แย่ที่สุดสำหรับประสิทธิภาพ
บทที่ 5: แนวคิดขั้นสูงและคำถามที่พบบ่อย
Key และ `React.Fragment`
บางครั้งคุณจำเป็นต้อง return อิลิเมนต์หลายตัวจาก `map` callback วิธีมาตรฐานในการทำเช่นนี้คือใช้ `React.Fragment` เมื่อคุณทำเช่นนี้ `key` จะต้องถูกวางไว้บนคอมโพเนนต์ `Fragment` เอง
function Glossary({ terms }) {
return (
<dl>
{terms.map(term => (
// key ต้องอยู่บน Fragment ไม่ใช่บน children
<React.Fragment key={term.id}>
<dt>{term.name}</dt>
<dd>{term.definition}</dd>
</React.Fragment>
))}
</dl>
);
}
สำคัญ: ไวยากรณ์แบบย่อ `<>...</>` ไม่รองรับ key หากลิสต์ของคุณต้องการ fragment คุณต้องใช้ไวยากรณ์ `<React.Fragment>` แบบเต็ม
Key ต้องการความไม่ซ้ำกันแค่ในหมู่พี่น้อง (Siblings) เท่านั้น
ความเข้าใจผิดที่พบบ่อยคือ key ต้องไม่ซ้ำกันทั่วทั้งแอปพลิเคชันของคุณ ซึ่งไม่เป็นความจริง Key ต้องการความไม่ซ้ำกันเพียงแค่ภายในลิสต์ของพี่น้องที่อยู่ติดกันเท่านั้น
function CourseRoster({ courses }) {
return (
<div>
{courses.map(course => (
<div key={course.id}> {/* Key สำหรับคอร์ส */}
<h3>{course.title}</h3>
<ul>
{course.students.map(student => (
// student key นี้ต้องการความไม่ซ้ำกันแค่ภายในลิสต์นักเรียนของคอร์สนี้เท่านั้น
<li key={student.id}>{student.name}</li>
))}
</ul>
</div>
))}
</div>
);
}
ในตัวอย่างข้างต้น คอร์สสองคอร์สที่แตกต่างกันอาจมีนักเรียนที่มี `id: 's1'` ได้ ซึ่งไม่มีปัญหาอะไรเลย เพราะ key กำลังถูกประเมินภายในอิลิเมนต์ `<ul>` ที่เป็น parent คนละตัวกัน
การใช้ Key เพื่อรีเซ็ต State ของคอมโพเนนต์โดยตั้งใจ
ในขณะที่ key มีไว้สำหรับการเพิ่มประสิทธิภาพของลิสต์เป็นหลัก มันยังมีจุดประสงค์ที่ลึกซึ้งกว่านั้น: มันกำหนดตัวตนของคอมโพเนนต์ หาก key ของคอมโพเนนต์เปลี่ยนไป React จะไม่พยายามอัปเดตคอมโพเนนต์ที่มีอยู่ แต่มันจะทำลายคอมโพเนนต์เก่า (และ children ทั้งหมด) และสร้างคอมโพเนนต์ใหม่ขึ้นมาแทนตั้งแต่ต้น ซึ่งจะเป็นการ unmount instance เก่าและ mount instance ใหม่ เป็นการรีเซ็ต state ของมันอย่างมีประสิทธิภาพ
นี่อาจเป็นวิธีที่ทรงพลังและเป็น declarative ในการรีเซ็ตคอมโพเนนต์ ตัวอย่างเช่น ลองนึกภาพคอมโพเนนต์ `UserProfile` ที่ดึงข้อมูลตาม `userId`
function App() {
const [userId, setUserId] = React.useState('user-1');
return (
<div>
<button onClick={() => setUserId('user-1')}>View User 1</button>
<button onClick={() => setUserId('user-2')}>View User 2</button>
<UserProfile key={userId} id={userId} />
</div>
);
}
โดยการวาง `key={userId}` บนคอมโพเนนต์ `UserProfile` เรารับประกันได้ว่าเมื่อใดก็ตามที่ `userId` เปลี่ยนไป คอมโพเนนต์ `UserProfile` ทั้งหมดจะถูกทิ้งและสร้างขึ้นใหม่ ซึ่งจะช่วยป้องกันบั๊กที่อาจเกิดขึ้นเมื่อ state จากโปรไฟล์ของผู้ใช้คนก่อน (เช่น ข้อมูลฟอร์ม หรือเนื้อหาที่ดึงมา) อาจยังคงค้างอยู่ เป็นวิธีที่สะอาดและชัดเจนในการจัดการตัวตนและ lifecycle ของคอมโพเนนต์
สรุป: การเขียนโค้ด React ที่ดีขึ้น
key
prop เป็นมากกว่าวิธีที่จะทำให้คำเตือนใน console หายไป มันเป็นคำสั่งพื้นฐานที่สำคัญต่อ React ซึ่งให้ข้อมูลที่จำเป็นสำหรับอัลกอริทึม reconciliation เพื่อให้ทำงานได้อย่างมีประสิทธิภาพและถูกต้อง การใช้งาน key อย่างเชี่ยวชาญเป็นเครื่องหมายของนักพัฒนา React มืออาชีพ
สรุปประเด็นสำคัญ:
- Key จำเป็นสำหรับประสิทธิภาพ: ช่วยให้อัลกอริทึม diffing ของ React สามารถเพิ่ม ลบ และจัดลำดับอิลิเมนต์ในลิสต์ได้อย่างมีประสิทธิภาพโดยไม่มีการแก้ไข DOM ที่ไม่จำเป็น
- ใช้ ID ที่เสถียรและไม่ซ้ำกันเสมอ: key ที่ดีที่สุดคือตัวระบุที่ไม่ซ้ำกันจากข้อมูลของคุณซึ่งไม่เปลี่ยนแปลงไปตามการ re-render
- หลีกเลี่ยงการใช้ array index เป็น key: การใช้ index ของรายการเป็น key อาจนำไปสู่ประสิทธิภาพที่ต่ำและบั๊กการจัดการ state ที่ละเอียดอ่อนและน่าหงุดหงิด โดยเฉพาะในลิสต์แบบไดนามิก
- อย่าใช้ key ที่เป็นค่าสุ่มหรือไม่เสถียรเด็ดขาด: นี่คือสถานการณ์ที่เลวร้ายที่สุด เพราะมันบังคับให้ React ต้องสร้างลิสต์ของคอมโพเนนต์ใหม่ทั้งหมดทุกครั้งที่ render ซึ่งทำลายทั้งประสิทธิภาพและ state
- Key กำหนดตัวตนของคอมโพเนนต์: คุณสามารถใช้ประโยชน์จากพฤติกรรมนี้เพื่อรีเซ็ต state ของคอมโพเนนต์โดยตั้งใจได้โดยการเปลี่ยน key ของมัน
เมื่อคุณเข้าใจหลักการเหล่านี้อย่างถ่องแท้ คุณจะไม่เพียงแต่เขียนแอปพลิเคชัน React ที่เร็วและเชื่อถือได้มากขึ้น แต่ยังเข้าใจกลไกหลักของไลบรารีอย่างลึกซึ้งยิ่งขึ้น ครั้งต่อไปที่คุณ map อาร์เรย์เพื่อแสดงผลลิสต์ อย่าลืมให้ความสำคัญกับ `key` prop อย่างที่มันควรจะได้รับ ประสิทธิภาพของแอปพลิเคชันของคุณ—และตัวคุณเองในอนาคต—จะขอบคุณคุณสำหรับสิ่งนี้