[React Inside Out] รู้จัก React Element, Component, Component Instance

Jirat Ki.
3 min readDec 10, 2016

ในช่วงหลายเดือนที่ผ่านมาหลายคนอาจจะได้ยิน React Fiber มาบ้าง หรือถ้าใครไปงาน ReactJS BKK 1.0.0 ช่วงท้ายๆ จะมีการเอ่ยถึง React Fiber ซึ่งเป็น Core Algorithm ตัวใหม่ของ React ที่กำลังพัฒนาอยู่ในตอนนี้

ส่วนตัวผมได้ยินชื่อ React Fiber มาซักพักใหญ่แล้ว แต่ไม่เคยเข้าไปทำความรู้จักมันแบบจริงจังสักที ส่วนใหญ่แค่อ่านหรือดูวิดีโอผ่านๆ แล้วพอดีน้อง Kanitkorn Sujautra โพส Video จากงาน ReactNext ทำให้ได้กลับไปดูและอ่านอีกรอบ

แต่ก่อนจะไปถึง React Fiber ผมอยากพูดถึงส่วนต่างๆ และการทำงานของ React โดยในบทความนี้ผมจะพูดถึง React Element, React Component และ Component Instance กันก่อน

เนื้อหาต่อไปนี้จะเป็นการอธิบายองค์ประกอบของ React นะครับ ไม่ได้สอนเขียน React Application — ดังนั้นอาจจะไม่เหมาะสำหรับคนที่กำลังมองหา How-to หรือ Tutorial อยู่

โดยปกติเราเขียน React แอพฯเราจะใช้งาน React Component, Instance และ Element อยู่แล้ว เพราะเวลาเราสร้างแอพพลิเคชั่นด้วย React เราจะสร้าง React Component ขึ้นมา (เช่น Button component) เมื่อแอพพลิเคชั่นเราทำงานเราก็จะมี Instance หลายๆ ตัวในแอพของเรา โดยแต่ละตัวจะมี State และ Properties ของมัน (เช่น ในหน้าจอเรามี Button Instance หลายอัน ซึ่งแต่ละอันสร้างมาจาก Button component อันเดียว แต่อาจมี Properties ที่ต่างกันออกไป)

โดยของที่ return มาจากฟังก์ชั่น render() หรือค่าที่ return จาก Stateless Component นั้นคือ Element

แต่สิ่งที่ออกมาจาก render() ยังไม่ใช่ HTML DOM Node ที่สามารถเอาไปแสดงได้ทันที แต่มันเป็นแค่ Plain JavaScript Object ธรรมดาอันนึงที่เก็บข้อมูลของสิ่งที่จะเอาไปแสดงผลบนจอ (ตรงนี้แหละที่เรียกว่า React Virtual DOM)

อย่าพึ่งสับสนระหว่าง React DOM Element กับ DOM Node สองอย่างนี้ไม่ใช่สิ่งเดียวกัน

React Element จะเป็นสิ่งที่เราบอกกับ React ว่าเราต้องการเห็นอะไรบนหน้าจอ แต่ตัวมันเองไม่ได้เป็นตัวที่แสดงบนหน้าจอ ตัวที่แสดงบนหน้าจอคือ DOM Node หรือ DOM Element ซึ่งจะเกิดขึ้นหลังจากสั่ง ReactDOM.render(Element, …)

React Element

อย่างที่บอก React Element เป็นแค่ JavaScript Object ที่เก็บข้อมูลสิ่งที่จะเอาไปแสดงผลบนหน้าจอ โดยมีข้อมูลสำคัญอยู่สองอย่างคือ Type และ Props

  • type — มีค่าเป็น String หรือ React Component Class | Function
  • props — เป็น JavaScript Object ที่เก็บค่าต่างๆ ของ Element ไว้
  • (มีนอกเหนือจากนี้ที่ไม่ได้พูดถึง)
{
type: 'div',
props: {
className: 'container'
}
}

โดยปกติ Element จะถูกสร้างผ่าน JSX หรือฟังก์ชั่น React.createElement ซึ่งเราทำกันเป็นปกติ

// JSX
<button color="blue">
<b>OK!</b>
</button>
// Compile to JS
React.createElement('button', { color: 'blue' },
React.createElement('b', null, 'OK!')
)

จากคำสั่งด้านบนเมื่อเราเรียก React.createElement เราจะได้ Element Object ออกมาประมาณนี้ (จริงไม่ใช่แบบนี้ซะที่เดียวนะ แค่เป็นตัวอย่าง)

{
type: 'button',
props: {
className: 'button',
children: {
type: 'b',
props: {
children: 'OK!'
}
}
}
}

แล้วพอเอาไป render ด้วยคำสั่ง ReactDOM.render(…)

ReactDOM.render({
type: 'button',
props: {
className: 'button',
children: {
type: 'b',
props: {
children: 'OK!'
}
}
}
}, ... )

เราก็จะได้ HTML DOM Node บนหน้าจอเรา

<button class='button'>
<b>
OK!
</b>
</button>

อย่างที่บอกไป type ของ Element นั้นสามารถเป็นได้ทั้ง String และ React Component ดังนั้นเราสามารถแยก React Element ได้เป็น 2 ประเภทคือ
DOM Element และ Component Element

DOM Element

React DOM Element คือ Element ที่มี type เป็น String โดยข้อมูลใน Element จะเป็นตัวบอก React ว่า DOM Node ที่จะแสดงผลหน้าตาเป็นยังไง
— ใช้ tag อะไร
— มี attributes อะไรบ้าง

{
type: 'button',
props: {
className: 'button',
children: {
type: 'b',
props: {
children: 'OK!'
}
}
}
}

อันนี้ตรงไปตรงมา เมื่อเราเอาไปแสดงผลสิ่งที่ได้คือ DOM Node <button> ที่มี <b> อยู่ข้างในและมีคำว่า “OK!” เหมือนกับตัวอย่างข้างบน

Component Element

Component Element คือ Element ที่มี type เป็น React Component Class ซึ่งมันถือว่าเป็น Element เหมือนกับ DOM Element แต่จะเก็บข้อมูลของ Component แทน

{
type: Button,
props: {
color: 'blue'
children: 'Danger!!'
}
}

จะเห็นว่าข้อมูลของ Element จะมี Type ที่เป็น Button ซึ่งจะไม่สามารถเอาไปสร้างเป็น DOM Node ได้

ซึ่งตรงนี้ถือ Core Idea ของ React เลย เพราะตัว Component จะซ่อนละเอียดของ Element Tree ภายในตัวมันไว้ โดยที่เราไม่ต้องสนใจว่ามันจะส่งอะไรออกมา อาจจะเป็น DOM Element หรือ Component Element อีกตัวก็ได้ ซึ่งส่วนนี้ React จะเป็นคนจัดการต่อ

React Component

For a React component, props are the input, and an element tree is the output.

จริงๆ แล้วเราสามารถเขียนแอพฯ โดยใช้เฉพาะ Element เลยก็ได้ แต่จะมีความยุ่งยากหน่อยเพราะต้องเขียน Element Tree ทั้งหมด ซึ่งตรงนี้แหละที่ Component เข้ามาแก้ปัญหา

โดย Component จะซ่อนรายละเอียดของ Element Tree เอาไว้ข้างใน โดยเมื่อเราส่ง Props ไปเป็น Input ให้กับมันเราจะได้ Element Tree ออกมา

ถ้าผมมี Component Button และ DangerButton แบบนี้

const Button = ({children, color}) => (
<button color={color}>
<b>{children}</b>
</button>
)
const DangerButton = ({children}) => (
<Button color='red'>{children}</Button>
)

เมื่อ Create Element ด้วยคำสั่ง

// JSX
<DangerButton color="red">Danger!!</DangerButton>
// Compile to JS
React.createElement(DangerButton, { color: 'red' }, 'Danger!!')

Element ที่ได้คือ

{
type: DangerButton,
props: {
color: 'red'
children: 'Danger!!'
}
}

และเมื่อ React จะเอา Element ไปแสดงผลและ React เห็นว่า Element นี้มี Type เป็น DangerButton ซึ่งเป็น Component ทำให้ตัว React ไม่สามารถเอาไปสร้าง DOM Node ได้

React ก็จะถาม DangerButton ว่าต้องการ Render อะไร ด้วย Props เหล่านี้ ซึ่ง DangerButton ก็จะส่ง Element Tree ออกมา

{
type: Button,
props: {
color: 'red',
children: 'Danger!!'
}
}

แต่ Element Tree ที่ได้มาก็ยังคงเป็น Component Element (Button) อยู่ เพราะงั้น React ก็จะถาม Button ต่อว่าต้องการ Render อะไร ซึ่งก็จะได้ Element Tree ต่อมา

{
type: 'button',
props: {
color: 'red',
className: 'button',
children: {
type: 'b',
props: {
children: 'Danger!!'
}
}
}
}

เมื่อถึงตรงนี้ React ก็จะได้ DOM Element ที่สามารถนำไปแสดงผลได้

ในโลกความจริงเราไม่ได้มี Component แค่ Component เดียวง่ายๆ แบบนี้ ดังนั้น React ก็จะไล่ตามไปเรื่อยๆ จนกว่าเราจะได้ Element Tree ทั้งหมดออกมาเพื่อใช้ในการแสดงผล ขั้นตอนเหล่านี้เราเรียกว่า Reconciliation ซึ่งจะเกิดขึ้นเมื่อเราเรียก
คำสั่ง ReactDOM.render(element, …) และเมื่อจบขั้นตอน React ก็จะรู้
Element Tree ทั้งหมดและใช้ ReactDOM หรือ ReactNative ในการสร้าง DOM Node เพื่อใช้ในการแสดงผลนั้นเอง

React Component Instance

Component Instance นั้นมีความสำคัญน้อยมากในงานของการใช้งาน React เนื่องจากตัว React จะคอยจัดการเกี่ยวกับพวก Instance ให้เราเอง ดังนั้นเราไม่จำเป็นต้องสร้าง Instance เอง (และไม่ควรสร้างเองด้วย)— ตัว Instance จะใช้สำหรับเก็บ State และจัดการ Lifecycle ต่างๆ

Component Instance จะถูกสร้างให้เฉพาะ Component ที่เป็น Class เท่านั้น (React.Component หรือ React.createClass) ส่วนของ Functional Component นั้นจะไม่มี Instance

อ้างอิง

--

--