โมเดลความคิดใน React ช่วยให้ React ดูเรียบง่าย: เรียนรู้แนวคิดสำคัญเบื้องหลังคอมโพเนนต์ การเรนเดอร์ สเตต และเอฟเฟกต์ แล้วนำไปใช้สร้าง UI อย่างรวดเร็วผ่านการแชท.

React อาจทำให้รู้สึกหงุดหงิดตอนแรกเพราะคุณเห็นหน้า UI เปลี่ยน แต่ไม่สามารถอธิบายได้เสมอไปว่าทำไมมันถึงเปลี่ยน คุณคลิกปุ่ม อะไรบางอย่างอัปเดต แล้วส่วนอื่นของหน้าก็ทำให้คุณประหลาดใจ นั่นไม่ใช่ว่า “React แปลก” เสมอไป แต่มักหมายถึง “ภาพที่ฉันมีในหัวเกี่ยวกับสิ่งที่ React กำลังทำมันไม่ชัด”
โมเดลความคิดคือเรื่องเล่าเรียบง่ายที่คุณบอกตัวเองเกี่ยวกับการทำงานของสิ่งหนึ่ง ถ้าเรื่องเล่านั้นผิด คุณจะตัดสินใจอย่างมั่นใจแต่ได้ผลลัพธ์ที่สับสน ลองคิดถึงเทอร์โมสตัท: โมเดลที่แย่คือ “ฉันตั้ง 22°C แล้วห้องจะเป็น 22°C ทันที” โมเดลที่ดีกว่าคือ “ฉันตั้งเป้าหมาย แล้วฮีตเตอร์จะเปิดปิดเป็นช่วง ๆ เพื่อเข้าใกล้มัน” ด้วยเรื่องเล่าที่ดีกว่า พฤติกรรมจะหยุดรู้สึกสุ่ม
React ก็ทำงานแบบเดียวกัน เมื่อคุณยึดแนวคิดที่ชัดเจนไม่กี่ข้อ React จะคาดเดาได้: คุณมองข้อมูลปัจจุบันแล้วเดาได้อย่างน่าเชื่อถือว่าจะเห็นอะไรบนหน้าจอ
Dan Abramov ช่วยทำให้แนวคิด "ทำให้คาดเดาได้" นี้เป็นที่แพร่หลาย เป้าหมายไม่ใช่จดจำกฎทั้งหมด แต่คือเก็บความจริงไม่กี่ข้อในหัวเพื่อให้คุณดีบักด้วยเหตุผล แทนการลองผิดลองถูก
ให้จำแนวคิดเหล่านี้ไว้ในใจ:
ยึดสิ่งเหล่านี้ไว้แล้ว React จะหยุดรู้สึกเหมือนเวทมนตร์ มันจะกลายเป็นระบบที่คุณวางใจได้
React ง่ายขึ้นเมื่อคุณหยุดคิดเป็น “หน้าจอ” แล้วเริ่มคิดเป็นชิ้นเล็ก ๆ คอมโพเนนต์คือหน่วย UI ที่นำกลับมาใช้ซ้ำได้ มันรับอินพุตแล้วคืนคำอธิบายว่าควรดูอย่างไรสำหรับอินพุตนั้น
ช่วยได้ถ้าคุณมองคอมโพเนนต์เป็นคำอธิบายบริสุทธิ์: “เมื่อมีข้อมูลแบบนี้ ให้แสดงแบบนี้” คำอธิบายนั้นนำไปใช้ได้หลายที่เพราะมันไม่ขึ้นกับที่ที่มันอยู่
Props คืออินพุต มาจากพาเรนท์ Props ไม่ได้เป็นของคอมโพเนนต์ และคอมโพเนนต์ไม่ควรเปลี่ยนมันเงียบ ๆ ถ้าปุ่มได้รับ label="Save" งานของปุ่มคือแสดง label นั้น ไม่ใช่ตัดสินใจให้มันต่างไป
State คือข้อมูลที่เป็นของคอมโพเนนต์ มันคือสิ่งที่คอมโพเนนต์จำได้เมื่อเวลาผ่านไป State เปลี่ยนเมื่อผู้ใช้โต้ตอบ เมื่อคำขอเสร็จ หรือเมื่อคุณตัดสินใจว่าควรต่างไป ไม่เหมือน props, state เป็นของคอมโพเนนต์นั้น (หรือคอมโพเนนต์ที่คุณเลือกให้เป็นเจ้าของ)
เวอร์ชันสั้นของแนวคิดหลัก: UI คือฟังก์ชันของ state ถ้า state บอกว่า “กำลังโหลด” ให้แสดงสปินเนอร์ ถ้า state บอกว่า “เกิดข้อผิดพลาด” ให้แสดงข้อความ ถ้า state บอกว่า “items = 3” ให้เรนเดอร์สามแถว งานของคุณคือทำให้ UI อ่านจาก state ไม่ใช่ลอยไปยังตัวแปรซ่อนเร้น
วิธีแยกความแตกต่างอย่างรวดเร็ว:
SearchBox, ProfileCard, CheckoutForm)name, price, disabled)isOpen, query, selectedId)ตัวอย่าง: modal พาเรนท์อาจส่ง title และ onClose เป็น props modal อาจมี isAnimating เป็น state
แม้เมื่อคุณกำลังสร้าง UI ผ่านแชท (เช่นบน Koder.ai) การแยกแยะนี้ยังเป็นวิธีที่เร็วที่สุดในการไม่สับสน: ตัดสินใจก่อนว่าอะไรเป็น props และอะไรเป็น state แล้วปล่อยให้ UI ทำตาม
วิธีเก็บ React ในหัวที่มีประโยชน์ (ในจิตวิญญาณของ Dan Abramov) คือ: การเรนเดอร์คือการคำนวณ ไม่ใช่งานทาสี React เรียกฟังก์ชันคอมโพเนนต์ของคุณเพื่อหาว่า UI ควรเป็นอย่างไรสำหรับ props และ state ปัจจุบัน ผลลัพธ์คือคำอธิบาย UI ไม่ใช่พิกเซล
การ re-render แค่หมายความว่า React ทำการคำนวณนั้นซ้ำ มันไม่ใช่ “หน้าทั้งหมดถูกวาดใหม่” React เปรียบเทียบผลลัพธ์ใหม่กับผลลัพธ์ก่อนหน้าและนำการเปลี่ยนแปลงที่เล็กที่สุดไปใช้กับ DOM จริง คอมโพเนนต์หลายตัวอาจ re-render ในขณะที่มีเพียงโหนด DOM จำนวนเล็กน้อยเท่านั้นที่อัปเดต
เหตุผลที่ทำให้เกิด re-render ส่วนใหญ่มีไม่กี่แบบง่าย ๆ: state ของคอมโพเนนต์เปลี่ยน, props เปลี่ยน, หรือพาเรนท์ re-render และ React ให้ลูกเรียก render อีกครั้ง ข้อสุดท้ายนี่มักทำให้คนประหลาดใจ แต่โดยทั่วไปมันไม่เป็นไร ถ้าคุณมองการเรนเดอร์เป็นสิ่งที่ “ถูกและน่าเบื่อ” แอปของคุณจะง่ายต่อการคิด
กฎนิ้วทองที่ทำให้สิ่งนี้ชัดเจน: ทำให้การ render เป็นบริสุทธิ์ เมื่อรับอินพุตเดียวกัน (props + state) คอมโพเนนต์ของคุณควรคืนคำอธิบาย UI เดียวกัน หลีกเลี่ยงความประหลาดใจภายใน render
ตัวอย่างชัดเจน: ถ้าคุณสร้าง ID ด้วย Math.random() ภายใน render การ re-render จะเปลี่ยนมัน และทันใดนั้นเช็คบ็อกซ์อาจเสียโฟกัสหรือไอเท็มในลิสต์ remount สร้าง ID ครั้งเดียว (state, memo, หรือนอกคอมโพเนนต์) แล้ว render จะเสถียร
ถ้าจำประโยคเดียวได้: การ re-render หมายถึง “คำนวณอีกครั้งว่า UI ควรเป็นอย่างไร” ไม่ใช่ “สร้างทุกอย่างใหม่หมด”
โมเดลช่วยอีกแบบ: การอัปเดต state คือคำขอ ไม่ใช่การกำหนดค่าแบบทันที เมื่อคุณเรียก setter อย่าง setCount(count + 1) คุณขอให้ React จัดคิวให้มีการเรนเดอร์ด้วยค่าที่ใหม่ ถ้าคุณอ่าน state ทันทีหลังจากนั้น คุณอาจยังเห็นค่าที่เก่าเพราะ React ยังไม่ได้เรนเดอร์
นั่นคือเหตุผลที่การอัปเดตแบบ “เล็กและคาดเดาได้” สำคัญ ชอบอธิบายการเปลี่ยนแปลงแทนการจับค่าที่คุณคิดว่าเป็นปัจจุบัน เมื่อค่าถัดไปขึ้นอยู่กับค่าก่อนหน้า ให้ใช้รูปแบบอัปเดต: setCount(c => c + 1) มันตรงกับวิธีที่ React ทำงาน: การอัปเดตหลายรายการสามารถถูกจัดคิว แล้วนำไปใช้ตามลำดับ
ความไม่เปลี่ยนรูป (immutability) คืออีกครึ่งของภาพ อย่าเปลี่ยนอ็อบเจ็กต์หรืออาร์เรย์ในที่เดียว สร้างอันใหม่ที่มีการเปลี่ยนแปลง React จะเห็นว่า “ค่านี้ใหม่” และสมองคุณจะตามรอยได้ว่ามีอะไรเปลี่ยน
ตัวอย่าง: การสลับสถานะ todo วิธีปลอดภัยคือสร้างอาร์เรย์ใหม่และอ็อบเจ็กต์ todo ใหม่สำหรับไอเท็มที่คุณเปลี่ยน วิธีเสี่ยงคือสลับ todo.done = !todo.done ภายในอาร์เรย์ที่มีอยู่
นอกจากนี้ ให้เก็บ state ให้น้อยที่สุดกับสิ่งที่จำเป็น กับกับดักทั่วไปคือเก็บค่าที่คุณคำนวณได้แล้ว ถ้าคุณมี items และ filter อยู่แล้ว อย่าเก็บ filteredItems ใน state คำนวณมันในระหว่างการเรนเดอร์ ตัวแปร state น้อยลงหมายถึงวิธีที่ค่าน้อยลงที่จะเบี้ยวออกจากกัน
การทดสอบง่าย ๆ ว่าสิ่งไหนควรอยู่ใน state:
ถ้าคุณสร้าง UI ผ่านแชท (รวมถึงบน Koder.ai), ให้ขอการเปลี่ยนแปลงเป็นแพตช์เล็ก ๆ: “เพิ่ม boolean แค่ตัวเดียว” หรือ “อัปเดตรายการนี้แบบไม่ทำลายเดิม” การเปลี่ยนแปลงเล็กและชัดเจนจะช่วยให้ตัวสร้างและโค้ด React ของคุณสอดคล้องกัน
การเรนเดอร์อธิบาย UI Effects ซิงค์กับโลกภายนอก "ภายนอก" คือสิ่งที่ React ไม่ได้ควบคุม: การเรียกเน็ตเวิร์ก, ไทเมอร์, API ของเบราว์เซอร์ และบางครั้งงาน DOM แบบอิมพีราเททีฟ
ถ้าบางอย่างคำนวณได้จาก props และ state มันมักไม่ควรอยู่ใน effect การใส่มันใน effect เพิ่มขั้นตอนที่สอง (เรนเดอร์, รัน effect, ตั้ง state, เรนเดอร์อีกครั้ง) ขั้นตอนพิเศษนี้คือจุดที่เกิดการกระพริบ การวนลูป และบั๊กแบบ “ทำไมค่านี้ถึงล้าสมัย?”
ความสับสนที่พบบ่อย: คุณมี firstName และ lastName แล้วเก็บ fullName ใน state โดยใช้ effect แต่ fullName ไม่ใช่ side effect มันคือข้อมูลที่คำนวณได้ คำนวณมันในระหว่างการเรนเดอร์แล้วมันจะตรงเสมอ
เป็นนิสัย: คำนวณค่าของ UI ในการเรนเดอร์ (หรือใช้ useMemo เมื่อแพงจริง ๆ), และใช้ effects สำหรับงาน "ทำอะไรบางอย่าง" ไม่ใช่ "คิดอะไรบางอย่าง"
มอง dependency array เป็น: “เมื่อค่านี้เปลี่ยน ให้รี-ซิงค์กับภายนอก” มันไม่ใช่เทคนิคประสิทธิภาพและไม่ใช่ที่สำหรับปิดคำเตือน
ตัวอย่าง: ถ้าคุณดึงรายละเอียดผู้ใช้เมื่อ userId เปลี่ยน ให้ใส่ userId ใน dependency array เพราะมันควรทริกเกอร์การซิงค์ ถ้าเอฟเฟกต์ใช้ token ด้วย ให้ใส่มันด้วย มิฉะนั้นคุณอาจดึงด้วย token เก่า
การเช็กที่ดี: ถ้าการลบเอฟเฟกต์จะทำให้ UI ผิดพลาด มันน่าจะไม่ใช่เอฟเฟกต์จริง ถ้าการลบมันจะหยุดไทเมอร์, ยกเลิกการสมัคร, หรือหยุดการ fetch มันน่าจะเป็นเอฟเฟกต์จริง
หนึ่งในโมเดลความคิดที่ใช้ได้จริงที่สุดคือ: ข้อมูลไหลลงต้นไม้ ส่วนการกระทำของผู้ใช้ไหลขึ้น
พาเรนท์ส่งค่าไปให้ลูก ลูกไม่ควรเป็นเจ้าของค่าเดียวกันสองที่อย่างลับ ๆ พวกมันร้องขอการเปลี่ยนแปลงโดยการเรียกฟังก์ชัน และพาเรนท์ตัดสินใจว่าค่าใหม่คืออะไร
เมื่อสองส่วนของ UI ต้องเห็นด้วยกัน ให้เลือกที่เดียวในการเก็บค่าแล้วส่งลงมา นี่คือการ "ยก state ขึ้น" มันอาจรู้สึกเหมือนงานเดินสายเพิ่ม แต่มันป้องกันปัญหาแย่กว่า: สอง state ที่เบี้ยวจากกันและบังคับให้คุณเพิ่มทางแก้เพื่อให้มันตรง
ตัวอย่าง: กล่องค้นหาและรายการผลลัพธ์ หากอินพุตเก็บ query ของตัวเองและลิสต์ก็เก็บ query ของตัวเอง ในที่สุดคุณจะเห็น "อินพุตแสดง X แต่ลิสต์ใช้ Y" การแก้คือเก็บ query ในพาเรนท์ ส่งมันให้ทั้งสอง และส่ง onChangeQuery(newValue) กลับไปที่อินพุต
การยก state ไม่ได้เป็นคำตอบเสมอไป ถ้าค่าหนึ่งสำคัญเฉพาะภายในคอมโพเนนท์ ให้เก็บไว้ที่นั่น การเก็บ state ใกล้จุดที่มันถูกใช้ทำให้โค้ดอ่านง่ายขึ้น
ขอบเขตปฏิบัติได้:
ถ้าคุณไม่แน่ใจว่าจะยก state ขึ้นหรือไม่ ให้มองหาสัญญาณเช่น: สองคอมโพเนนท์แสดงค่าเดียวกันในแบบต่างกัน; การกระทำที่หนึ่งต้องอัปเดตสิ่งที่อยู่ไกล; คุณคัดลอก props มาลง state “กันไว้”; หรือคุณเพิ่มเอฟเฟกต์เพียงเพื่อทำให้สองค่าตรงกัน
โมเดลนี้ยังช่วยเมื่อสร้างผ่านเครื่องมือแชทอย่าง Koder.ai: ขอเจ้าของเดียวสำหรับแต่ละชิ้นของ shared state แล้วสร้าง handlers ที่ไหลขึ้น
เลือกฟีเจอร์ขนาดพอเหมาะที่คุณจับไว้ในหัวได้ ตัวอย่างที่ดีคือรายการที่ค้นหาได้ และคลิกไอเท็มเพื่อดูรายละเอียดใน modal
เริ่มโดยสเก็ตช์ส่วน UI และเหตุการณ์ที่อาจเกิดขึ้น อย่าคิดถึงโค้ดตอนนี้ คิดถึงสิ่งที่ผู้ใช้ทำได้และสิ่งที่พวกเขาเห็น: มีช่องค้นหา, รายการ, ไฮไลต์แถวที่เลือก, และ modal เหตุการณ์คือพิมพ์ค้นหา, คลิกไอเท็ม, เปิด modal, ปิด modal
ตอนนี้ “วาด state” เขียนค่าจำนวนน้อยที่ต้องเก็บ และตัดสินใจว่าใครเป็นเจ้าของกี่ค่า กฎง่าย ๆ ใช้ได้ดี: พาเรนท์ร่วมที่ใกล้ที่สุดของทุกที่ที่ต้องการค่าควรเป็นเจ้าของมัน
สำหรับฟีเจอร์นี้ state ที่เก็บอาจเล็ก: query (string), selectedId (id หรือ null), และ isModalOpen (boolean). ลิสต์อ่าน query และเรนเดอร์ไอเท็ม Modal อ่าน selectedId เพื่อแสดงรายละเอียด ถ้าทั้งลิสต์และ modal ต้องการ selectedId, เก็บมันในพาเรนท์ ไม่ใช่ทั้งสองที่
ต่อมา แยกข้อมูลที่คำนวณได้ออกจากข้อมูลที่เก็บ รายการที่กรองแล้วเป็นข้อมูลที่คำนวณได้: filteredItems = items.filter(...) อย่าเก็บมันใน state เพราะมันคำนวณได้เสมอจาก items และ query การเก็บข้อมูลที่คำนวณได้เป็นสาเหตุที่ค่ามักเบี้ยว
จากนั้นถาม: เราต้องใช้เอฟเฟกต์ไหม? ถ้า items อยู่ในหน่วยความจำอยู่แล้ว ก็ไม่ต้อง ถ้าการพิมพ์ควร fetch ผล ให้ใช้เอฟเฟกต์ ถ้าการปิด modal ควรบันทึกบางอย่าง ให้ใช้เอฟเฟกต์ Effects ใช้สำหรับซิงค์ (fetch, save, subscribe) ไม่ใช่สำหรับการเชื่อมต่อ UI พื้นฐาน
สุดท้าย ทดสอบการไหลด้วยกรณีขอบ:
selectedId ยังใช้งานได้หรือไม่?\n- ปิด modal: เก็บการเลือกไว้หรือรีเซ็ต?\n- ไอเท็มรีเฟรชจากเซิร์ฟเวอร์: ถ้าไอเท็มที่เลือกหายไป จะเกิดอะไรขึ้น?\n
ถ้าคุณตอบคำถามพวกนี้บนกระดาษ โค้ด React มักจะตรงไปตรงมาความสับสนส่วนใหญ่ใน React ไม่ใช่เรื่องไวยากรณ์ แต่มันเกิดเมื่อโค้ดของคุณไม่ตรงกับเรื่องเล่าสั้น ๆ ในหัว
เก็บ derived state. คุณเก็บ fullName ใน state ทั้งที่มันคือ firstName + lastName มันทำงานจนกว่าฟิลด์หนึ่งเปลี่ยนและอีกฟิลด์ไม่เปลี่ยน และ UI จะแสดงค่าล้าสมัย
เอฟเฟกต์วนลูป. เอฟเฟกต์ fetch ข้อมูล ตั้ง state และ dependency ทำให้มันรันอีกครั้ง อาการคือคำขอซ้ำ ๆ UI สั่น หรือ state ที่ไม่เคยนิ่ง
ปิดทับด้วยค่าเก่า (stale closures). handler คลิกอ่านค่าที่เก่า (เช่นเคาน์เตอร์หรือตัวกรองที่ล้าสมัย) อาการคือ “ผมคลิกแล้ว แต่มันใช้ค่าของเมื่อวาน”
global state ทุกที่. ใส่ทุกรายละเอียด UI ลงในสโตร์กลางทำให้ยากที่จะรู้ว่าใครเป็นเจ้าของอะไร อาการคือคุณเปลี่ยนอย่างหนึ่งแล้วสามหน้าจอตอบสนองแบบไม่คาดคิด
แก้ไขวัตถุซ้อนทับ. คุณอัปเดตอ็อบเจ็กต์หรืออาร์เรย์ในที่เดียวแล้วสงสัยว่าทำไม UI ไม่อัปเดต อาการคือ “ข้อมูลเปลี่ยน แต่ไม่มีการ re-render”
ตัวอย่างชัดเจน: แผง “ค้นหาและจัดเรียง” ถ้าคุณเก็บ filteredItems ใน state มันอาจเบี้ยวจาก items เมื่อข้อมูลใหม่มาถึง แทนที่จะเก็บผลที่กรองแล้ว ให้เก็บอินพุต (search text, sort choice) แล้วคำนวณลิสต์ในเรนเดอร์
กับเอฟเฟกต์ ให้เก็บไว้สำหรับการซิงค์กับโลกภายนอก (fetching, subscriptions, timers). ถ้าเอฟเฟกต์ทำงาน UI พื้นฐาน มันมักจะเป็นงานที่ควรอยู่ใน render หรือ event handler
เมื่อสร้างหรือแก้ไขโค้ดผ่านแชท ข้อผิดพลาดเหล่านี้จะปรากฏเร็วขึ้นเพราะการเปลี่ยนแปลงอาจมาพร้อมกันเป็นก้อนนิสัยที่ดีคือถามคำถามเชิงความเป็นเจ้าของ: “แหล่งความจริงสำหรับค่านี้คืออะไร?” และ “เราคำนวณค่านี้ได้ไหม แทนที่จะเก็บมัน?”
เมื่อ UI เริ่มไม่คาดเดาได้ มักไม่ใช่เพราะ React เยอะไป แต่มักเป็นเพราะ state เยอะไป อยู่ผิดที่ หรือนำไปทำงานที่มันไม่ควรทำ ก่อนจะเพิ่ม useState อีกตัว หยุดคิดและถาม:\n\n- ชุดค่าน้อยที่สุดที่บรรยายได้ครบว่าผู้ใช้เห็นอะไรตอนนี้คืออะไร?\n- แต่ละค่าควรอยู่ที่ไหนเพื่อให้ส่วนที่ต้องใช้มันอ่านได้ และส่วนที่ไม่ต้องการไม่ถูกรบกวน?\n- ค่านี้เป็นข้อมูลใหม่จริงหรือคำนวณได้จาก props, พารามิเตอร์ URL, หรืstate อื่น?\n- คุณใช้เอฟเฟกต์เพื่อคำนวณ UI หรือแค่ซิงค์กับสิ่งที่อยู่นอก React (เน็ตเวิร์ก, ไทเมอร์, API ของเบราว์เซอร์)?\n- ถ้าค่าหนึ่งเปลี่ยน คอมโพเนนท์ไหนควร re-render เพราะอ่านมัน และอันไหนไม่ควร?\n
ตัวอย่างเล็ก ๆ: search box, filter dropdown, list ถ้าคุณเก็บทั้ง query และ filteredItems ใน state คุณมีสองแหล่งความจริง แทนที่จะเก็บทั้งสอง ให้เก็บ query และ filter แล้วคำนวณ filteredItems ในเรนเดอร์
นี่สำคัญเมื่อคุณสร้างอย่างรวดเร็วผ่านเครื่องมือแชท ความเร็วดี แต่ให้ถามเสมอว่า: “เราเพิ่ม state หรือเราเผลอเพิ่มค่าที่คำนวณได้?” ถ้ามันคำนวณได้ ให้ลบ state นั้นและคำนวณมันแทน
ทีมเล็กกำลังสร้าง UI แอดมิน: ตารางคำสั่งซื้อ, ฟิลเตอร์บางอย่าง, และไดอะล็อกแก้ไขคำสั่งซื้อ คำขอแรกมักคลุมเครือ: “เพิ่มฟิลเตอร์และ popup แก้ไข” ฟังดูง่าย แต่บ่อยครั้งมันจะกลายเป็น state กระจัดกระจายทั่ว
ทำให้มันเป็นรูปธรรมโดยแปลงคำขอเป็น state และ events แทนคำว่า “ฟิลเตอร์” ให้ตั้งชื่อ state: query, status, dateRange แทน “popup แก้ไข” ให้ตั้งชื่อ event: “ผู้ใช้คลิก Edit บนแถว” แล้วตัดสินใจว่าใครเป็นเจ้าของแต่ละส่วน (หน้า, ตาราง, หรือไดอะล็อก) และอะไรคำนวณได้ (เช่น filtered list)
ตัวอย่าง prompt ที่รักษาโมเดลไว้ (ใช้ได้ดีกับตัวสร้างแชทอย่าง Koder.ai):
OrdersPage that owns filters and selectedOrderId. OrdersTable is controlled by filters and calls onEdit(orderId).”visibleOrders from orders and filters. Do not store visibleOrders in state.”EditOrderDialog that receives order and open. When saved, call onSave(updatedOrder) and close.”filters to the URL, not to compute filtered rows.”หลังจาก UI ถูกสร้างหรืออัปเดต ให้ตรวจทานแบบด่วน: แต่ละค่า state มีเจ้าของหนึ่งเดียว ค่าที่คำนวณได้ไม่ได้ถูกเก็บ เอฟเฟกต์ใช้เพื่อซิงค์กับภายนอกเท่านั้น (URL, เครือข่าย, storage) และเหตุการณ์ไหลลงเป็น props และไหลขึ้นเป็น callbacks
เมื่อ state คาดเดาได้ การวนปรับจะปลอดภัย คุณสามารถเปลี่ยนเลย์เอาต์ตาราง เพิ่มฟิลเตอร์ หรือปรับฟิลด์ในไดอะล็อกโดยไม่ต้องเดาว่ามี state ซ่อนอะไรจะพัง
ความเร็วมีประโยชน์เมื่อแอปยังง่ายจะคิด การป้องกันที่ง่ายที่สุดคือใช้โมเดลความคิดเหล่านี้เป็นเช็คลิสต์ก่อนคุณเขียน (หรือสร้าง) UI
เริ่มแต่ละฟีเจอร์แบบเดียวกัน: เขียน state ที่ต้องการ เหตุการณ์ที่จะเปลี่ยนมัน และใครเป็นเจ้าของ ถ้าคุณพูดไม่ได้ว่า “คอมโพเนนท์นี้เป็นเจ้าของ state นี้ และเหตุการณ์เหล่านี้อัปเดตมัน” คุณอาจจบลงด้วย state กระจัดกระจายและ re-render ที่น่าตกใจ
ถ้าคุณสร้างผ่านแชท ให้เริ่มด้วยโหมดวางแผน อธิบายคอมโพเนนท์ รูปร่างของ state และการเปลี่ยนแปลงเป็นภาษาง่าย ๆ ก่อนขอโค้ด เช่น: “แผงฟิลเตอร์อัปเดต state query; รายการผลลัพธ์คำนวณจาก query; การเลือกไอเท็มตั้ง selectedId; การปิดล้างมัน” เมื่ออ่านแล้วสะอาด การสร้างโค้ดจะเป็นขั้นตอนกลไก
ถ้าคุณใช้ Koder.ai (koder.ai) เพื่อสร้างโค้ด React ควรตรวจสอบอย่างรวดเร็ว: เจ้าของชัดเจนสำหรับแต่ละค่า state, UI คำนวณจาก state, เอฟเฟกต์เฉพาะสำหรับการซิงค์, และไม่มีแหล่งความจริงซ้ำ
แล้ววนปรับทีละน้อย หากต้องการเปลี่ยนโครงสร้าง state (เช่นจาก boolean หลายตัวเป็นฟิลด์ status เดียว) ให้จับสแนปชอตก่อน ทดลอง แล้วย้อนกลับถ้าโมเดลความคิดแย่ลง และเมื่อคุณต้องการรีวิวเชิงลึกหรือส่งต่อ การส่งออกซอร์สโค้ดช่วยให้ตอบคำถามจริง ๆ ได้ง่ายขึ้น: รูปร่าง state ยังบอกเรื่องราวของ UI อยู่หรือไม่?
จุดเริ่มต้นที่ดีคือ: UI = f(state, props). คอมโพเนนต์ของคุณไม่ได้ “แก้ไข DOM”; พวกมัน อธิบาย ว่าควรแสดงอะไรบนหน้าจอตามข้อมูลปัจจุบัน ถ้าหน้าจอดูผิด ให้ตรวจสอบ state/props ที่สร้างหน้าจอนั้น แทนที่จะตรวจสอบ DOM
Props คืออินพุต จากพาเรนท์; คอมโพเนนต์ควรถือว่ามันเป็นแบบอ่านอย่างเดียว. State คือความจำ ที่คอมโพเนนต์เป็นเจ้าของ (หรือคอมโพเนนต์ที่คุณเลือกให้เป็นเจ้าของ). ถ้าค่าหนึ่งต้องแชร์ ให้ยกขึ้นไปข้างบนและส่งลงมาเป็น props
การ re-render หมายถึง React เรียกฟังก์ชันคอมโพเนนต์ของคุณซ้ำ เพื่อคำนวณคำอธิบาย UI ถัดไป มันไม่จำเป็นต้องหมายถึงการวาดหน้าใหม่ทั้งหมด React จะอัปเดต DOM จริงด้วยชุดการเปลี่ยนแปลงที่เล็กที่สุดที่ต้องใช้
เพราะการอัปเดต state เป็นการ ถูกจัดคิว ไม่ใช่การกำหนดค่าโดยทันที ถ้าค่าถัดไปขึ้นอยู่กับค่าก่อนหน้า ให้ใช้รูปแบบอัปเดตเพื่อไม่พึ่งค่าที่อาจล้าสมัย:\n\n- setCount(c => c + 1)\n\nวิธีนี้จะถูกต้องแม้ว่าจะมีการจัดคิวอัปเดตหลายรายการ
หลีกเลี่ยงการเก็บสิ่งที่คุณคำนวณได้จากอินพุตที่มีอยู่ เก็บ อินพุต แล้วคำนวณส่วนที่เหลือในระหว่างการเรนเดอร์\n\nตัวอย่าง:\n\n- เก็บ: items, filter\n- คำนวณ: visibleItems = items.filter(...)\n\nวิธีนี้ป้องกันไม่ให้ค่าล้าสมัยหรือเบี้ยวจากกัน
ใช้เอฟเฟกต์เพื่อ ซิงค์กับสิ่งที่ React ไม่ได้ควบคุม: การดึงข้อมูลเครือข่าย, การสมัคร, ไทม์เมอร์, API ของเบราว์เซอร์ หรือการทำงานเชิงอิมพีราเททีฟกับ DOM\n\nอย่าใช้เอฟเฟกต์เพียงเพื่อคำนวณค่าของ UI จาก state—ให้คำนวณในระหว่างการเรนเดอร์ (หรือใช้ useMemo ถ้ามันแพงจริง ๆ)
มอง dependency array เป็น: “รายการทริกเกอร์ เมื่อค่ายกต่างไป ให้รี-ซิงค์กับภายนอก” มันไม่ใช่เทคนิคเพื่อประสิทธิภาพและไม่ควรใช้เพื่อปิดคำเตือน\n\nตัวอย่าง: ถ้าคุณดึงข้อมูลผู้ใช้เมื่อ userId เปลี่ยน ให้ใส่ userId ในอาร์เรย์เพราะมันควรเป็นทริกเกอร์ ถ้าเอฟเฟกต์ใช้ token ด้วย ให้ใส่ด้วย มิฉะนั้นอาจดึงข้อมูลด้วย token เก่าได้
ถ้าสองส่วนของ UI ต้องเห็นด้วยกันเสมอ ให้เก็บ state ไว้ที่ พาเรนท์ร่วมที่ใกล้ที่สุด แล้วส่งค่าไปด้านล่างและส่ง callback ขึ้นมา\n\nการทดสอบด่วน: ถ้าคุณสำเนาค่าตัวเดียวกันในสองคอมโพเนนต์และเขียนเอฟเฟกต์เพื่อ “เก็บให้ตรงกัน” ค่านั้นน่าจะต้องมีเจ้าของเดียว
มักเกิดขึ้นเมื่อ handler จับค่าจากเรนเดอร์ก่อนหน้า วิธีแก้ที่พบบ่อย:\n\n- ใช้ setter แบบอัปเดต: setX(prev => ...)\n- เก็บค่าที่เปลี่ยนใน state/refs แทนตัวแปรธรรมดา\n- ให้เอฟเฟกต์ระบุค่าที่มันอ่าน\n\nถ้าการคลิกใช้ “ค่าของเมื่อวาน” ให้สงสัยเรื่อง stale closure
เริ่มด้วยแผนเล็ก ๆ: คอมโพเนนต์ ใครเป็นเจ้าของ state และมีเหตุการณ์อะไรบ้าง แล้วค่อยสร้างโค้ดทีละน้อยเป็น แพตช์เล็ก (เพิ่มฟิลด์ state หนึ่งตัว, เพิ่ม handler หนึ่งตัว, คำนวณค่าหนึ่งค่า) แทนการเขียนทับใหญ่ครั้งเดียว\n\nถ้าคุณใช้ตัวสร้างโค้ดแบบแชทอย่าง Koder.ai ให้ขอ:\n\n- เจ้าของ state ที่ชัดเจนหนึ่งคนต่อค่าหนึ่งค่า\n- ค่าที่คำนวณได้ให้คำนวณในเรนเดอร์\n- เอฟเฟกต์เฉพาะสำหรับการซิงค์ (fetch/URL/storage) ไม่ใช่สายงาน UI พื้นฐาน\n\nวิธีนี้ช่วยให้โค้ดที่สร้างยังสอดคล้องกับโมเดลความคิดของ React