ข้ามไปยังเนื้อหา

Why React & the Model

ลองนึกถึง counter เวอร์ชัน imperative บอก browser ว่าจะ update อย่างไร ทีละขั้น:

// Imperative: you manage the DOM and keep it in sync by hand.
let count = 0;
const label = document.querySelector("#count");
document.querySelector("#inc").addEventListener("click", () => {
count += 1;
label.textContent = count; // you must remember to do this, everywhere
});

จุดที่เกิด bug คือส่วน “ต้องจำว่าต้อง update” — ทุกที่ที่เปลี่ยน count ก็ต้อง update ทุกจุดของ UI ที่ขึ้นกับค่านั้นด้วย พลาดจุดเดียวหน้าจอก็โกหกทันที

เวอร์ชัน declarative อธิบายว่า UI เป็นอะไร สำหรับ state หนึ่ง ๆ และไม่แตะ DOM เลย:

// Declarative: describe the UI for the current state; React syncs the DOM.
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

ไม่มีขั้น “update label” คุณแค่เปลี่ยน count และเพราะ UI ถูก อธิบาย ในรูปของ count React จึง re-render และหน้าจอถูกต้องโดยอัตโนมัติ bug ประเภท “ลืม update X” หายไปทั้งกลุ่ม

React component คือฟังก์ชันที่รับ props (input จาก parent) และใช้ state (ความจำของตัวเอง) แล้ว return คำอธิบายของ UI

// props flow in; state lives inside; the return describes the UI.
function Greeting({ name }) { // name is a prop
const [count, setCount] = useState(0); // count is state
return (
<div>
<p>Hello, {name}</p>
<button onClick={() => setCount(count + 1)}>Clicked {count} times</button>
</div>
);
}
flowchart LR
  props["props (from parent)"] --> comp["Component function"]
  state["state (own memory)"] --> comp
  comp --> ui["returns: UI description
(elements)"]
  ui --> react["React renders it"]
component แปลง input เป็นคำอธิบาย UI

สองกฎที่ตามมาจาก “component คือฟังก์ชัน”:

  • props เป็น read-only component ต้องไม่แก้ props ของตัวเอง — เป็นของ parent ข้อมูลไหล ลง
  • input เดียวกัน output เดียวกัน เมื่อ props และ state เท่าเดิม component ควร render สิ่งเดิม นั่นคือข้อกำหนดเรื่อง purity (มีบทเรียนเต็ม ๆ ทีหลัง) และเป็นสิ่งที่ทำให้ React รัน component ซ้ำได้อย่างอิสระ

ทุกอย่างที่เหลือใน React เป็นผลพวงของ UI = f(state):

  • hooks มีไว้เพื่อให้ function component มี state และ lifecycle โดยไม่ ทำลาย model “เป็นแค่ฟังก์ชัน”
  • reconciliation มีไว้เพื่อแปลง “นี่คือคำอธิบาย UI ใหม่” ให้เป็น “นี่คือการแก้ DOM น้อยที่สุด”
  • concurrent features (transitions, Suspense) มีอยู่ได้เพราะ f(state) ที่ pure สามารถถูกคำนวณ หยุด และ restart ได้อย่างปลอดภัย

คุณไม่ได้กำลังเรียน API กองใหญ่ คุณกำลังเรียนสมการเดียวและผลของตัวเอง

ปัญหาหลักของการ update DOM แบบ imperative ที่ React แก้ให้คืออะไร?
input ของ React component คืออะไร?
ข้อใดจริงเกี่ยวกับ props?