Container (Components ตัวแม่), PropTypes, และ Component ที่เขียนแบบ Function

เนื้อหาของคอร์สนี้ เรียบเรียงขึ้นจาก React.js Fundamental
บทความนี้เราจะทำความเข้าใจในเรื่อง ดังต่อไปนี้

  1. Container หรือที่เราเคยเรียกว่า component ตัวแม่ ซึ่งทำหน้าที่เป็น container สำหรับวาง component ลูกๆ อยู่ภายใน
  2. PropTypes อันเป็นตัวที่ใช้ตรวจสอบ data type ของ props ของแต่ละ component ซึ่งมีประโยชน์มากสำหรับ dynamic launguage อย่าง JavaScript
  3. Component ที่เขียนแบบ Function (State Functional) ซึ่งโดยปรกติแล้วเวลาเราสร้าง component เราจะสร้างแบบ component ตามบทความก่อนหน้านี้ แต่จริงๆ แล้วเราสามารถสร้าง component ได้อีกแบบ ในแบบที่น่าจะคุ้นเคยกับสาย developer มากกว่า เพราะมันเป็นวิธีการสร้าง component โดยเขียนแบบ function แทนที่จะเป็น component

Container

ก่อนอื่นเรามาว่าวกันเรื่อง Container ก่อนเลย

ตัว Container นี้ แท้ที่จริงแล้วก็คือ Component ตัวแม่ที่ใช้ในการ “ห่อหุ้ม” ตัวลูก เพราะตัว React จะมองทุกอย่างเป็น Component และเวลาวาง component ก็วางซ้อนกันไป ซ้อนกันมา Component ตัวไหนที่เอาไว้ใช้วาง component ตัวอื่น เราจะเรียกว่า container กัน

มาเริ่มสร้าง container ตัวแรกกันเลย

ให้สร้าง folder ที่ชื่อว่า containers ภายใต้ folder app (app/containers)

จากนั้นให้สร้างไฟล์ชื่อว่า PromptContainer.js ภายในใต้ containers (app/containers/PromptContainer.js)

ก่อนให้เราสร้าง “โครง” ของ component ขึ้นมาก่อน ดังนี้

app/containers/PromptContainer.js

เวลาเราสร้าง component ทุกครั้ง ให้เราขึ้นโครงแบบ Code ข้างต้นก่อน จะได้ทำงานง่ายๆ โดยแบ่งเป็นสามส่วนคือ

  1. ส่วน react import
  2. ส่วนของ component
  3. ส่วน export

หลังจากนั้นให้เติม code ในส่วนของ component ซึ่งเป็น form ธรรมดาๆ ดังนี้

หากผู้อ่านมีพื้นหลังจาก HTML มาอยู่แล้ว component ตัวนี้ก็ไม่มีอะไรต้องอธิบายมาก ก็แค่ form หนึ่งตัวที่มี text input หนึ่งตัว และปุ่มกด submit หนึ่งปุ่มเท่านั้น เพียงแต่มีการอ้างอิง class ของ bootstrap เท่านั้น และอีกประการก็คือ เนื่องคำว่า class ใน javascript เป็นคำสงวน (reserved word) ดังนั้น ใน React จึงใช้คำว่า class ไม่ได้ จำเป็นต้องใช้คำว่า className ในเวลาที่ต้องการอ้างถึง CSS class

จากนั้นให้เรา Set ให้ router รู้จักกับ PromptContainer ตัวนี้

ให้เราเปิดไฟล์ app/config/routes.js แล้วแก้ Code ดังนี้

ในที่นี้เราเพิ่ม child route เข้าไปอีกหนึ่งตัวโดยชื่อ path ว่า playerOne

เมื่อเราลองเข้าไปที่ localhost:8080/playerone (อย่าลืม start server ด้วยนะครับ) จะเจอกับหน้าดังต่อไปนี้

screen-shot-2559-11-03-at-5-08-38-pm

เปลี่ยน header text ของหน้า /playerone

คราวนี้เราอยากจะเปลี่ยนคำว่า ‘HEADER TEXT’ ให้เป็นคำที่เราต้องการในรูปแบบ dynamic ก็สามารถทำได้ ผ่าน react-router โดยให้เปิดไฟล์ app/config/routes.js ขึ้นมา แล้วแก้ code ดังนี้

ตอนนี้เราได้เพิ่ม props ให้กับ route ไปหนึ่งตัว คราวนี้ เราอยากจะรู้ว่า เมื่อถึงเวลาที่ต้องเข้าไปที่ PromptContainer เราจะดึงค่าจาก header ใน route ได้อย่างไร

วิธีก็ง่ายๆ ครับ ให้เปิดไฟล์ app/containers/PromptContainer.js ขึ้นมา แล้วใส่ console.log เข้าไปดังนี้

จากนั้นเข้าไปที่ localhost:8080/playerone จากนั้นให้ไปดู console ของ browser เพื่อดูว่า PromptContainer พิมพ์อะไรออกมาที่ console บ้าง ซึ่งจะได้ตามภาพ ดังนี้

screen-shot-2559-11-03-at-5-22-43-pm

จากรูปจะเห็นว่า ตัว props.route นั้น จะมีค่า header เพิ่มเข้ามา โดยมีค่าเท่ากับ ‘Player One’ ตามที่ได้เซตเอาไว้ที่หน้า routes.js

ดังนั้น ให้ทำการ ลบ console.log ออกไปจากหน้า PromptContainer.js แล้วเปลี่ยนค่า HEADER TEXT เป็นคำว่า {this.props.route.header} ซึ่งผลจะได้เป็นไปตาม code ดังนี้

เมื่อลองเข้าไปที่ localhost:8080/playerone หน้าตาของ component จะเปลี่ยนไปตามนี้

screen-shot-2559-11-03-at-5-27-36-pm

ตกแต่ง component ด้วย javascript

หากสังเกตให้ดี PromptContainer จะมีพื้นสีเทา ซึ่งในที่นี้เราอยากเปลี่ยนให้มันเป็นสีโปร่งใส ดังนั้น เราจึงต้องใช้ style เข้ามาช่วย

เริ่มจากเพิ่ม folder ให้กับ app ชื่อว่า styles/ แล้วเพิ่มไฟล์ที่ชื่อว่า index.js (app/styles/index.js) โดยภายในไฟล์ดังกล่าว ให้เพิ่ม code ดังนี้

วิธีในการตกแต่ง component ของ React ก็คล้ายๆ กับ CSS แถม attribute ต่างๆ ก็เหมือนกันเกือบหมดด้วยซ้ำ อย่างใน code ข้างต้น เราใช้ background attribute สำหรับกำหนดคุณสมบัติให้กับพื้นหลังของ component เป็นต้น จุดที่แตกต่างก็คือ แทนที่จะสร้างไฟล์ CSS เราจะเลือกใช้ javascript module แทน (Javascript จะต้องมีการ exports ตัว content ออกมา)

คราวนี้ให้เราเปิดไฟล์ app/components/PromptContainer.js แล้วแก้ code ดังนี้

คราวนี้ลองเข้าไปที่ localhost:8080/playerone ใหม่ จะเห็นว่า background สี่เทาหายไปแล้ว

screen-shot-2559-11-03-at-6-08-27-pm

แก้ style ให้ Home component

ให้เปิดไฟล์ app/components/Home.js ขึ้นมาแล้วแก้ไฟล์ตามนี้

Code ข้างบนมีเรื่องสองเรื่อง

  1. การ import ของ styles นั้น สามารถระบุชื่อ property ข้างในได้เลย อย่างในที่นี้คือ require(‘../styles’).transparentBg จะเป็นการ import module ชื่อ styles แล้วอ้าง property ที่ชื่อ transparentBg ตรงบรรทัด import ทันที ดังนั้น variable ตัวนั้นจึงสามารถนำมาใช้ได้ตรงๆ
  2. Link component ของ react-router module ที่ทำหน้าที่ในการ link ไปหา component ตามที่ map ไว้ใน router ในไฟล์ app/config/routes.js ซึ่งในที่นี้จะ route ไปหา path /playerone และเมื่อนำ Link component ไปครอบอะไรไว้ เวลาไปคลิกมัน ก็เท่ากับเป็นการพาตัวเองไป path ที่กำหนดไว้ใน link จาก code ข้างต้น เป็นการนำ Link ที่จะไปยัง path /playerone มาครอบ button ไว้ ดังนั้น เมื่อใดที่กดปุ่มดังกล่าว มันจะ route ไปยังหน้า /playerone

ให้เราลองเข้าไปที่ localhost:8080

screen-shot-2559-11-03-at-6-25-04-pm

หากเราลองกดปุ่ม Get Started มันก็จะพาเราไปยังหน้า /playerone ทันที

Component State

คราวนี้เราจะมาคุยกันถึงเรื่องสำคัญของ React กันแล้ว ซึ่งก็คือ state นั่นเอง

state นั้นมีหน้าที่ในการเก็บข้อมูลต่างๆ ของหน้า UI ที่พร้อมจะเปลี่ยนแปลงอยู่ตลอดเวลา โดยหาก component ใดต้องการกำหนด state ของตัวเองขึ้นมา ให้เริ่มกำหนดจาก function ที่ชื่อว่า getInitialState (หากใครมีพื้นมาจาก OOP ฟังก์ชั่นนี้ก็คือ constructor ของแต่ละคลาสนั่นแหละครับ)

ให้เราเปิด app/containers/PromptContainer.js ออกแล้วแก้ code ดังต่อไปนี้

จาก code ข้างต้นเราได้สร้าง state ขึ้นมาหนึ่งตัวคือ username โดยตั้งค่าเริ่มต้นในฟังก์ชั่น getInitialState โดยเริ่มต้นด้วยการ assign empty string เข้าไป นั่นแปลว่า state ที่ชื่อ username พร้อมใช้งานแล้ว

จากนั้นเราก็ทำดัก event onChange ของ text input ใน component โดยสร้างฟังก์ชั่น onUpdateUser ขึ้นมารองรับ โดยภายในฟังก์ชั่นดังกล่าวผมได้ลองใส่ console.log เข้าไปเพื่อตรวจดูว่า รายละเอียดของ event object ที่ถูกยิงเข้ามายัง event handler function ตัวนี้จะมีค่าเป็นอย่างไร ซึ่งเมื่อลองเปิดหน้า browser  ไปยัง localhost:8080/playerone แล้วเปิด console ขึ้นมา จากนั้นลองพิมพ์ตัวอักษรลงไปใน text input หลักบนหน้าดังกล่าว เราจะเห็นข้อมูลไหลตามภาพต่อไปนี้

screen-shot-2559-11-03-at-7-24-37-pm

จะเห็นว่า event.target นั้นจะมีข้อมูลดังที่เห็น และข้อมูลที่เราต้องการก็คือ event.target.value ซึ่งก็คือค่า text บน text input นั่นเอง โดยเมื่อไหร่ก็ตามที่เราต้องการ set ค่าให้กับ state เราต้องใช้คำสั่ง this.setState() สำหรับการนี้

ให้เราทำการลบ console.log ในฟังก์ชั่น onUpdateUser ออกไปก่อน

ตัวสุดท้ายที่อยากเน้นก็คือ ตัว defaultValue เป็น attribute ที่แสดงว่าค่า default ของ text input ต้องมีค่าเป็นอะไร ซึ่งในที่นี้เราได้กำหนดให้เป็น state.username นั่นเอง

คราวนี้เราอยากจะใส่ even handler ให้กับ form submit event ซึ่งเป็น event ที่จะเกิดขึ้นหลังจากการ submit form โดยให้แก้ code ดังนี้

เริ่มต้นของ onSubmitUser นั้นเราจะสร้าง variable ชื่อว่า username เพื่อรับค่า state.username ซึ่งถูก set ค่าจาก text input จากนั้นก็จะทำการ reset ค่าของ state ไป

หน้าที่ของ event handler ที่ชื่อ onSubmitUser ก็คือ หลังจากเรา submit form แล้ว มันจะพาเราไปยังหน้าต่อไป ซึ่งระบบที่เรากำลังจะสร้างนี้คือระบบ github battle คือการนำ user ใน github สองคนมาแข่งกัน แล้วตัดสินว่าใครได้คะแนนดีกว่า

ดังนั้น path ที่จะต้องมีแน่ๆ คือ

/playerOne  สำหรับกรอก github username ของคนแรก

/playerTwo สำหรับกรอก github username ของคนที่สอง

/battle สำหรับแสดงผลการตัดสิน

ดังนั้น ภายใน function onSubmitUser จะต้องตรวจสอบ path ณ ปัจจุบันก่อนว่าเป็น path อะไร เพื่อจะได้ตัดสินต่อไปว่า จะไปที่ path อะไรต่อ ผมจึงทำการ console.log ไว้ในฟังก์ชั่นดังกล่าว เพื่อเช็คว่า props ของหน้าปัจจุบันนี้มีอะไรบ้างที่เราพอจะเอามาใช้เช็ค path ในปัจจุบันได้ ให้เราลอง browse ไปที่ localhost:8080/playerOne แล้วกรอกข้อมูลเข้าไปใน text input จากนั้น กดปุ่ม submit ให้ปิด console ออกมา จะได้ผลลัพธ์เป็นดังนี้

screen-shot-2559-11-04-at-6-47-06-am

จะเห็นว่าตอนนี้เรามี props ชื่อว่า routeParams ซึ่งใช้สำหรับเก็บค่า parameter จาก url path และเราต้องการจะเก็บค่า username ของ player one ไว้ใน routeParams ดังนั้น เราต้องไปสร้าง route ตัวที่สองใน app/config/routes.js ดังนี้

เราได้เพิ่ม path playerTwo/ เข้ามา ซึ่งก็ใช้ PromptContainer เป็น component สำหรับ path นี้เหมือนกับ playerOne/ ต่างกันตรงที่ มาคราวนี้ เรามีตัวประหลาดโผล่ขึ้นมา นั่นคือ

ใน path นั้น คำใดก็ตามที่ขึ้นต้นด้วย ‘:’ ให้ถือว่าคำนั้นเป็น route parameter ทั้งหมด ดังนั้น แปลว่า หากเมื่อใดที่เรา route ไปที่ /playerTwo/himaeng นั่นแปลว่า ค่า route parameter ที่ชื่อว่า playerone จะเท่ากับ ‘himaeng’ ไปในทันที

ดังนั้นให้เรากลับมาที่ app/containers/PromptContainer.js แล้วทำการแก้ไข code ดังนี้

ไล่ทีละบรรทัด

บรรทัดนี่คือการเซตค่าเพื่อใช้สำหรับทำ dynamic routing ให้กับ react-router เมื่อเราเพิ่มบรรทัดนี้เข้าไปใน component เราจะสามารถสั่งให้ route ไปที่ path ก็ได้ภายใน component

ซึ่งจริงๆ แล้วตัว context มันเป็น props ที่อยู่ใน React อยู่แล้ว เพียงแต่การจะ access ตัว context ได้นั้น เราจะต้อง “ส่งผ่าน” ค่าของมันผ่านการใช้ props ตั้งแต่ระดับยอดสุดของ component ไล่ลงมาจนถึง component ปัจจุบัน (หากยังจำกันได้ react จะส่งผ่านข้อมูลไปยัง  component โดยใช้ props มิฉะนั้น component ตัวลูกจะมอง props ดังกล่าวไม่เห็น)

ซึ่งการทำเช่นนั้น จะยุ่งยากมาก แล้วยิ่งหากระบบของเราใหญ่ และซับซ้อนมากขึ้นเท่าไหร่ การที่เราต้องมานั่งเขียน code เพื่อส่งผ่าน context ทุกครั้งก็ดูจะไม่ค่อยสมเหตุสมผลซักเท่าไหร่

ดังนั้นทาง React จึงช่วยให้เราทำงานง่ายขึ้น ผ่านการเขียน code ในบรรทัดดังกล่าว

เป็นการเช็คว่า ตอนนี้ route parameter ที่ชื่อว่า playerOne มีค่าหรือยัง ซึ่งจะเกิดขึ้นได้ ต้องมีการ submit หน้า form บน path playerOne/ เสียก่อน ตามตัวอย่างข้างบน ดังนั้น หาก playerOne มีค่า นั่นแปลว่า ตอนนี้เราอยู่ใน path playerTwo/ เรียบร้อยแล้ว หน้าที่ของระบบก็คือ route ไปยัง path สุดท้ายนั่นคือ battle/

code ในส่วนนี้จะแสดงวิธีการทำงานของ context ว่าทำงานอย่างไร การ route ไปยังหน้าอื่นนั้น เราจะทำผ่าน context.router.push() โดยฟังก์ชั่นนี้จะรับ parameter สองตัว กล่าวคือ ตัวแรกเป็น pathname ที่จะต้องไป ส่วนตัวที่สองคือ query หรือ param สำหรับ path นั้นๆ

จาก code ข้างต้น จะเห็นได้ว่า pathname ก็คือ /battle โดยมี param สองตัว นั่นคือ playerOne และ playerTwo

ใน case ที่ค่า this.props.routeParams.playerOne มีค่าเป็น null แปลว่า ตอนนี้เราอยู่ใน path playerOne/ ดังนั้น หน้าที่ของเราคือการ route ไปยัง path playerTwo/ พร้อมด้วย username ที่จะนำไปแทนค่าเป็น :playerOne ใน route parameter สำหรับ path playerTwo/ อีกด้วย

คราวนี้ให้เรา browse ไปยังหน้า localhost:8080/playerone ใหม่ แล้วกรอกข้อมูล กดปุ่ม continue ก็จะเห็นว่ามันจะพาเราไปยังหน้า localhost:8080/playertwo/<player one’s username> แต่คราวนี้หากเราลองกรอกข้อมูลในหน้านี้ แล้วกดปุ่ม continue ผลคือ มันจะแสดง error ใน console ดังนี้

screen-shot-2559-11-04-at-7-15-13-am

error ดังกล่าวแจ้งให้เรารู้ว่า ตอนนี้เรายังไม่ได้กำหนด route สำหรับ /battle เลย มันก็เลยไม่รู้จะไปไหนต่อ

สำหรับตอนนี้ เรายังจะไม่ทำอะไรกับตัว path /battle เพราะจะเป็นเรื่องที่เราจะทำต่อใน tutorial ต่อจากนี้ ตอนนี้ของติดเรื่องนี้ไว้ก่อน

Refactoring

เมื่อเราทำจน feature มัน work แล้ว ก็ควรที่จะทำการ refactoring code ให้มันมีความมาตรฐาน สั้น กระชับ และสามารถนำไปขยายต่อไปได้

การ refactoring ครั้งนี้ จะโฟกัสไปที่ app/container/PromptContainer.js เป็นหลัก (ก็งานเรามันหนักไปที่ PromptContainer นี่นา) หากเราเปิด code ดังกล่าวดูจะเห็นว่า code ดังกล่าวจะแบ่งเป็นสองส่วนใหญ่ๆ กล่าวคือ logic กับ UI ดังภาพ

screen-shot-2559-11-04-at-7-25-00-am

เราจะทำการแยกส่วนของ UI ไปอยู่ใน components/ แล้วให้ container เป็นตัวเก็บ logic เพื่อใช้ควบคุม UI component ต่อไป

แยก UI ไปใส่ components/

ก่อนอื่นให้เราไปส่งไฟล์ที่ชื่อว่า app/components/Prompt.js ขึ้นมา แล้วกรอก code เข้าไปดังนี้

คือพูดง่ายๆ ให้เรา copy เฉพาะส่วน ของ UI มาแปะไว้ใน Prompt.js นั่นเอง

จากนั้นให้เราเปิดไฟล์ app/containers/PromptContainer.js แล้วแก้ code ให้เป็นดังนี้

ครั้งนี้เราแก้แค่จุดเดียว นั่นคือ เอา Prompt ใส่เข้าไปแทน UI ก่อนหน้านี้ (ซึ่งเราได้ทำการโยกไปใส่ Prompt.js เรียบร้อยแล้ว)

ส่งผ่านข้อมูลจาก PromptContainer ไปสู่ Prompt

คราวนี้เราจะทำการส่งผ่าน function ที่จำเป็นสำหรับการใช้งานของ Prompt.js (ซึ่งตอนนี้ function ทั้งหมดอยู่ภายใน PromptContainer.js ทั้งหมด) ทั้งหมดผ่าน props (อย่าลืมนะครับ การส่ง props ไปยัง component ก็เหมือนกับการส่ง argument ไปยัง function)

ให้เปิดไฟล์ app/container/PromptContainer.js ขึ้นมา แล้วแก้ไขดังนี้

คราวนี้เท่ากับว่า Prompt ของเรานี้มี props เกิดขึ้น 4 ตัวแล้ว (ขออนุญาติเปลี่ยนชื่อ function จาก onSubmitUser เป็น handleSubmitUsere และ onUpdateUser เป็น hanleUpdateUser เพื่อให้เหมาะสมกับการใช้งาน)

ตอนนี้ Container ของเราก็ทำหน้าที่เพียงสองอย่าง นั่นคือ

  1. คอยจัดการข้อมูลใน state ผ่านฟังก์ชั่น getInitialState กับ handleUpdateUser
  2. จัดการเรื่องการ routing ผ่านฟังก์ชั่น handleSubmitUser

ส่วนเรื่องของการทำ UI rendering จะถูกแยกหน้าที่ออกไปอยู่ที่ Prompt component (หรือตัวลูกของมัน) แทน

เท่ากับว่า เราได้ทำการแยกส่วนการทำงานระหว่าง business logic กับ UI กันเป็นที่เรียบร้อยแล้ว

Refactoring UI component

คราวนี้เรามาทำการ refactoring ส่วนของ UI component กันบ้าง

ให้เปิดไฟล์ app/components/Prompt.js ขึ้นมา แล้วแก้ไข code ดังนี้

นี่คือการ refactor เล็กๆ เพื่อให้ Prompt ใช้งาน props ที่ถูกส่งต่อมาจาก ตัวแม่ หรือ container ของมันได้ (ผมจะพยายามใช้คำว่า container แทนตัวแม่ไปเรื่อยๆ นะครับ จนว่าคุณผู้อ่านจะคุ้นเคย)

ให้เราลองทดสอบเข้าไปที่ localhost:8080 อีกครั้ง ว่ามันใช้งานได้หรือไม่ (หากเจอ error ลองหาจุดผิดพลาด แล้วทำการแก้ไขดู โดย error จาก console เป็นเครื่องนำทาง ถือเป็นการฝึกความเข้าใจใน React ไปอีกทางหนึ่งด้วย)

หากคุณหาทางแก้ไม่ได้จริงๆ ก็สามารถเข้าไปดู code ต้นแบบได้ที่
https://github.com/himaeng/react-fun-course/tree/master/04-refactoring

ใช้ PropTypes เพื่อเช็ค DataTyps ของ props

จากการสื่อสารระหว่าง Prompt และ PromptContainer จะเห็นว่ามีการส่ง props จากแม่ไปหาลูกอยู่ตลอดเวลา คำถามคือ เราจะแน่ใจได้อย่างไรว่า datatype ของ props แต่ละตัวนั้นมีอะไรบ้าง

ดังนั้น เราจะต้องทำการตรวจเช็คก่อนว่า props ที่ส่งเข้ามานั้นมีอะไรบ้างเพื่อประโยชน์สองสถาน

  1. ประกันความถูกต้องของ Data Type
  2. เป็น document ให้แต่ละ component ว่ามี props อะไรบ้าง (ไม่งั้นเราจะไม่มีทางรู้เลยว่าแต่ละ component มีอะไรบ้าง)

เปิดไฟล์ app/components/Prompt.js ขึ้นมาแล้วแก้ไข code ดังนี้

PropTypes เป็นฟีเจอร์ของ React เพื่อทำสิ่งที่เราต้องการ ในที่นี้จะเห็นว่า เราสามารถเห็นได้ในจุดเดียวเลยว่า ตอนนี้ Prompt component ของเรามี props อะไรบ้าง และแต่ละตัวมี data type เป็นอะไร (func ใช้สำหรับ function)

isRequired ใช้สำหรับแจ้งว่า props นี้เป็นสิ่งที่ require สำหรับ component นี้ หากตัวแม่ (container) ไม่ส่งข้อมูลมา component จะแจ้ง error ให้ผู้ใช้ได้ทราบ

ให้ลองทดสอบกับ browser ว่าทุกอย่างยังทำงานได้ปรกติเหมือนเดิมหรือไม่ (เหมือนเดิม หากเจอ error ลองแก้ไขด้วยตัวเองดูก่อน เพื่อเสริมสร้างความเข้าใจในบทเรียน)

Refactoring Component to Stateless Functional

หัวข้อสุดท้ายในวันนี้ก็คือ การสร้าง component ในแบบ stateless functional ซึ่งเป็นวิธีในการสร้าง component ในแบบที่เราในฐานะ developer น่าจะคุ้นเคยกันดี เพราะแทนที่จะสร้าง component ด้วยการใช้ React.createClass() แล้วใช้ function render() ในการส่ง UI ออกมาจาก component เราก็สร้าง function ตรงๆ ขึ้นมาเลย โดยไม่ต้องใช้ React.createClass และ render() function

ให้เปิดไฟล์ app/components/Prompt.js ขึ้นมาแล้วแก้ code เป็นดังนี้

จากนั้นให้ copy content ใน render function ตั้งแต่บรรทัด

 

แล้วนำไปวางไว้ใน function Prompt (props) จะทำให้เรา code ดังนี้

สังเกตให้ดีๆ นะครับ ตัว Prompt function นั้น รับ props มาเป็น argument อยู่แล้ว ดังนั้น สิ่งใดก็ตามภายใน function นี้ ที่ต้องมีการอ้างอิงถึง props ก็ให้เรียกใช้ props ได้โดยตรง แทนที่จะเป็น this.props ดังนั้น เราจะสามารถแก้ไข code ได้ดังนี้

ขั้นตอนสุดท้ายคือ refactor ส่วนของ propTypes โดยให้อ้าง propTypes คือ properties ของ Prompt function ซึ่งเขียนได้ดังนี้

ต่อไปนี้คือ code ตัวสมบูรณ์ของ app/components/Prompt.js

โดยสรุป วิธีการสร้าง component แบบ stateless functional นั้นก็คือ การสร้าง function แล้ว return ค่าออกมาเป็น UI โดยรับ argument เป็น props เท่านั้นเอง ซึ่งก็จะให้ผลลัพธ์ไม่ต่างจากการสร้าง component แบบเดิม เพียงแต่แบบนี้ จะเป็นสิ่งที่ดูเข้าใจง่ายกว่าสำหรับ developer เท่านั้น

หากไม่มีอะไรผิดพลาด เมื่อเปิดไปยัง localhost:8080 จากนั้นลองใช้ระบบดู ทุกอย่างจะต้องทำงานได้เหมือนเดิม

สำหรับผู้ที่ต้องการ code สำหรับอ้างอิง สามารถเข้าไปดูได้ที่ github ตาม link นี้

https://github.com/himaeng/react-fun-course/tree/master/05-stateless-functional

สรุป

วู้ว!!! (ปาดเหงื่อแปบ)

นี่คือ toturial ที่ยาวมาก (แต่ไม่ต้องห่วง ต่อจากนี้ก็จะมีที่ยาวกว่านี้อีก ดังนั้นทำตัวให้ชินซะ 555) โดยเนื้อหาจะเป็นเรื่องการอธิบายถึงการแยกระบบ React ออกเป็นสองส่วนใหญ่ ได้แก่ Container กับ Component โดย Container นั้นคือตัวที่ใช้เป็นตัวแม่ของ component

หน้าที่ของ Container คือจัดการกับ business logic ส่วน component ก็ดูแลเรื่องการ render UI ไป

การสร้าง component นั้น สามารถสร้างได้สองแบบ คือ Component กับ Stateless Functional โดยแบบหลังจะเหมาะกับ developer มากกว่า (ในมุมมองของผม)

นอกจากนั้นเรายังมีเรื่องของ PropTypes ที่มาช่วยเรื่องของการเช็ค data type ของ props และเป็น document ให้กับ component ของเราทางอ้อมได้อีกด้วย

 

1 COMMENT