Nesting Components, Props, and Pure Functions

เนื้อหาของคอร์สนี้ เรียบเรียงขึ้นจาก React.js Fundamental
เนื้อหาในบทความตอนที่แล้วได้กล่าวถึงวิธีในการสร้าง React Component ขั้นต้นไปแล้ว มาถึงตอนนี้เราจะมาพูดถึงเรื่องของการสร้าง component แบบต่อกันเป็นชั้น หรือที่เรียกในภาษาอังกฤษว่า Nesting Components ซึ่งประโยชน์ของการเรียง component ต่อกันเป็นชั้นๆ นั้น ก็คือ เราสามารถส่งผ่าน data จาก component ตัวแม่ ไปยังตัวลูก หรือตัวหลาน เหลน โหลน ได้อย่างง่ายดาย

React มีจุดเด่นในเรื่องของการจัดการ state หรือข้อมูลที่ใช้สำหรับ UI ได้เป็นอย่างดี และหนึ่งในความสามารถที่ React มีก็คือ การส่งผ่านข้อมูลจาก component แม่ ไปยัง component ลูก หลาน เหลน โหลน นั่นเอง ซึ่ง data เหล่านี้จะใช้สิ่งที่เรียกว่า props เป็นเครื่องมือในการส่ง

จำคำนี้ไว้นะครับ…

การส่ง props ไปยัง component ก็เหมือนกับการส่ง argument ไปยัง function

มาดูตัวอย่างกัน

screen-shot-2559-11-01-at-1-23-56-pm

จากภาพจะเห็นว่า component ที่ชื่อ HelloUser นั้น รอรับค่า props ที่มีชื่อว่า name โดยระบุเป็น this.props.name ดังนั้น เวลาเรียกใช้ <HelloUser /> ก็จะมีการใส่ค่า props เข้าไป โดยจากตัวอย่างข้างเราจะใส่ค่า name=”Tyler” เข้าไป

หากดูให้ดี การส่ง props เข้าไปใน component ก็มีลักษณะที่คล้ายคลึงกับการส่ง argument ให้กับ function นั่นเอง โดยภายใน function (หรือ component) จะเอา argument (หรือ props) นั้นๆ ไปใช้งานต่อภายในของ function นั้นๆ

เราลองมาดูตัวอย่างที่ซับซ้อนกว่าเดิม คราวนี้เรามี component สองตัว ตัวแรกเป็น component แม่ ส่วนอีกตัวเป็น component ลูก ตัวแม่ส่งต่อข้อมูลไปยังลูกในรูปแบบของ props โดย code ต่อไปนี้จะแสดงตัวอย่างให้เห็นจริง

Component ตัวแม่

และนี่คือ Component ตัวลูก

Component ที่ชื่อว่า FriendsContainer หรือตัวแม่นั้น ภายในตัวมันจะมี Component ShowList (ตัวลูก) อยู่ภายใน โดยให้สังเกตว่าจะมีการนำ array ที่ชื่อว่า friends ใส่ลงไปเป็น props ของ ShowList ที่มีชื่อว่า names

คราวนี้ names ซึ่งเป็น props ของ ShowList จะมีค่าเป็น array [‘Ean Platter’, ‘Murphy Randall’, ‘Merrick christensen’]

จุดที่น่าสนใจของ code ข้างต้นอยู่ตรงที่ ฟังก์ชั่น map ของ array จาก code

ฟังก์ชั่น map นั้นเป็นฟังก์ชั่นมาตรฐานของ javascript มีหน้าที่ในการนำสมาชิกแต่ละตัวของ array ต้นทาง (ในที่นี้คือ names) มารันตาม callback function ซึ่งจากตัวอย่างข้างต้น จะเป็นการนำชื่อภายใน array names แต่ละตัว มาครอบด้วย tag <li></li> จากนั้นก็นำไปสร้างเป็น array ใหม่ที่ชื่อว่า listItems ด้วยเหตุนี้ ค่าของ listItems จะมีค่าในตอนสุดท้ายเป็นดังนี้

หากเราติดตาม tutorial ชุดนี้ไปซักพักหนึ่ง จะเริ่มเห็นว่า ฟังก์ชั่น map นี้ เป็นฟังก์ชั่นที่เหมาะกับการทำงานในสไตล์ของ React เป็นอย่างยิ่ง เพราะมันช่วยให้เราสามารถวนลูปสมาชิกแต่ละตัวภายใน array แล้วสร้างเป็น ui ออกมาได้อย่างสะดวกง่ายดาย

สร้าง UI ด้วยหลักการ Pure Function ใน React

เราทุกคนในฐานะ developer ต่างก็คุ้นเคยแนวคิดของการใช้ function กันดีอยู่แล้ว เราสามารถใช้ความรู้ดังกล่าวมาใช้งานใน React ได้อย่างง่ายดาย เพราะการสร้าง UI ภายใน React นั้น ก็คือแนวคิดเดียวกับการใช้งาน function นั่นเอง

React.js นั้น หากมองดีๆ เราจะสามารถนำสิ่งที่เราคุ้นเคยเป็นอย่างดีอยู่แล้วมาใช้งานได้โดยตรง นั่นคือ แนวคิดเรื่องของฟังก์ชั่น นั่นเพราะวิธีในการสร้างหรือใช้งาน React Component นั้น ไม่ได้ต่างไปจากการสร้างหรือใช้งานฟังก์ชั่นเลย ต่างกันเพียงแต่ว่า ฟังก์ชั่นรับอาร์กิวเม้นท์ แล้วคืนผลลัพธ์ออกมาเป็น value ส่วน React Component นั้น รับ props แล้วคืนค่าออกมาเป็น UI

มองแบบนี้แล้วก็ทำใจให้รัก React ง่ายขึ้นิดนึง เพราะมันทำให้เราสร้าง UI ได้ง่ายขึ้น โดยใช้หลักคิดที่ฝังแน่นอยู่ในสมองของชาว dev. ทั้งหลาย

ลองมาดูตัวอย่างจริงของ React กันเลย

code ข้างต้นนั้น มีการสร้าง component แม่ขึ้นมาหนึ่งตัว กล่าวคือ Avatar และ component ลูกมาสองตัวซึ่งก็คือ ProfilePic กับ ProfileLink โดย ตัว component นั้นจะรับค่า props ที่ชื่อว่า username อีกทีหนึ่ง แล้วหลังจากนั้น ก็จะทำการส่ง props ไปยัง component ลูกทั้งสองตัวต่อ ผ่าน code ดังนี้

คำถามคือ แล้ว Avatar ซึ่งเป็นตัวแม่นั้น เอาค่า this.props.username มาจากไหน คำตอบก็คือ ใน code ข้างต้นมีการเรียกใช้ Avartar ในบรรทัดสุดท้าย ดังนี้

บรรทัดนี้เป็นการเรียกใช้งาน component โดยมีการ assign ค่าให้กับ props ของ Avatar ที่ชื่อว่า username ให้มีค่าเป็น “tylermcginnis” ดังนั้น ตัว Avatar จึง props ที่จะนำไปส่งต่อให้กับ Component ลูกทั้งสองตัวได้

แต่คราวนี้ แทนที่เราจะไปเดือดร้อนสร้าง component ให้ยุ่งยาก React เปิดให้เราสามารถสร้าง function ที่รับค่า props แล้วคืนค่าออกมาเป็น ui ได้เช่นกัน ซึ่งให้ผลเหมือนกับการสร้าง component แบบ code ข้างต้น โดยสามารถเขียนใหม่ได้ดังนี้

เพียงเท่านั้น เราก็สามารถสร้าง UI โดยการเขียน code ที่สั้นลงได้แล้ว (สรุปคือ React เปิดให้เราสามารถเลือกเขียนตัวสร้าง UI ได้ทั้งสองแบบทั้งแบบ component กับ function)

แต่มีอยู่สิ่งนึงที่ทั้ง component กับ function ข้างต้นเหมือนกันก็คือ ทั้งสองแบบต่างก็ใช้วิธีการเขียนที่เรียกว่า pure function

React ใช้งาน Pure Function  เป็นหลัก ซึ่งวิธีการนี้เป็นวิธีการเขียนโปรแกรมในแบบ Functional Programming (ดังนั้น การเรียน  React ในช่วงแรกๆ จะดูงงๆ หน่อย ก็ไม่แปลก เพราะวิธีการเขียนโปรแกรมจะมีการเปลี่ยนไปจากที่เราคุ้นเคยไม่มากก็น้อย)

ประโยชน์หลักๆ ของการเขียนโปรแกรมในรูปแบบของ pure function ก็คือ มันมีความ consistency (ความสม่ำเสมอ) และ predictability (ผลลัพธ์มีความถูกต้องสูง) ที่เป็นเช่นนี้เพราะ pure function มีคุณสมบัติดังต่อไปนี้

  • Pure function จะ return ค่าเดิมเสมอ หากเราใส่ arguments เหมือนเดิม
  • Pure function จะไม่ขึ้นกับ state ของ application ที่เปลี่ยนแปลงอยู่ตลอด
  • Pure function จะไม่แก้ข้อมูลของ variable ที่อยู่ภายนอก scope ภายใน function ของตน

โดยสรุปก็คือ pure function นั้นจะเป็นฟังก์ชั่นที่เราสามารถรู้คำตอบล่วงหน้าได้จากการดูข้อมูลใน argument

ยกตัวอย่างเช่น code ดังต่อไปนี้

จะเห็นว่าฟังก์ชั่น add ถือเป็น pure function เพราะมันไม่มีผลกระทบกับ variable ข้างนอก และมันจะสร้างผลลัพธ์ที่เหมือนเดิมเสมอ หากเรากรอก argument ตัวเดิม

คราวนี้มาดูตัวอย่างจริงของ build-in function ของ Javascript กันดูบ้าง โดยในที่นี้จะดูที่ฟังก์ชั่น slice() กับ splice

จะเห็นว่า slice เป็น pure function เพราะเมื่อใดก็ตามที่เรากรอก argument เหมือนเดิม เราก็จะได้ผลลัพธ์เหมือนเดิม

คราวนี้มาดูกันที่ฟังก์ชั่น splice()

ฟังกชั่นนี้ไม่ถือเป็น pure function เพราะถึงแม้เราจะกรอก argument เหมือนเดิมทุกครั้ง แต่ผลลัพธ์ที่ได้จะไม่เหมือนเดิม ที่สำคัญคือ มันเป็นฟังก์ชั่นที่เข้าไปแก้ไข state ของ array ต้นทาง (ในที่นี้คือ friends)

ว่าแต่ไอเจ้า pure function นี่มันสำคัญกับ React ยังไง? ตอบแบบกำปั้นทุบดินก็คือ render() ฟังก์ชั่นของ React มันเป็น pure function อีกประการหนึ่ง การฝึกให้ตัวเองเขียน pure function จนคล่องนั้น จะทำให้คุณกลายเป็น developer ที่ดีขึ้นอีกด้วย และการจะเขียน  React ให้ถึงระดับเทพนั้น pure function คือบันไดก้าวแรกสู่สิ่งนั้นนั่นเอง

เริ่มต้นใช้งาน Pure Function

ให้เปิดไฟล์ app/index.js ขึ้นมา แล้วเพิ่ม code ดังต่อไปนี้

จากนั้นให้เราลองรัน server ผ่านคำสั่ง

เมื่อลองเปิด browser แล้วเข้าไปที่ localhost:8080 จากนั้นคลิกขวาแล้วเลือก inspect (ใน chrome) คุณจะเห็นว่า ในหน้า console จะแสดงผลดังรูป

screen-shot-2559-11-01-at-3-56-42-pm

ซึ่งแสดงทั้ง anySortOfData และ name ซึ่งเป็น data ของ props ใน Hello component

คราวนี้เราเปลี่ยน code ใหม่ ดังนี้

จากนั้นให้เราไป refresh ที่หน้า browser เราจะต้องเห็น text ที่เปลี่ยนไปเป็น Hello Tyler

สร้าง Nesting Component

ถึงเวลาสร้าง Nesting Component กันแล้ว ให้เปิดไฟล์ app/index.js ขึ้นมาแล้วแก้ไข code ในไฟล์ให้เป็นไปตามข้างล่างนี้ (แก้เกือบหมดไฟล์)

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

เวลาเราจะสร้าง object อะไรขึ้นมาใน javascript สามารถทำได้ง่ายมาก โดยให้ดูจากบรรทัดแรกของ code ข้างต้น

จะเห็นว่าเราแค่ครอบด้วย { } แล้วภายในนั้นก็ใส่ข้อมูลต่างๆ ลงไป ในรูปแบบ key: value เพียงเท่านี้ก็เสร็จแล้ว

อีกประการหนึ่ง เมื่อใดก็ตามที่มีการอ้างถึง variable ภายใน React จะต้องอ้างอิงอยู่ภายใต้ { } เช่นกัน ดังเช่น

(จริงๆ แล้วตามมาตรฐานของ React ที่แนะนำจริงๆ ก็คือ แม้แต่ string ก็ควรจะวางอยู่ภายใต้ { } เช่นเดียวกัน ตัวอย่างเช่น {‘Hello World!’} เป็นต้น แต่การไม่ครอบก็ไม่ได้ทำผิดอะไร)

มี code อยู่บรรทัดนึงที่ผมอยากจะเน้น

ตรงส่วนของ style จะเห็นว่ามีการครอบ {{ }} สองชั้น สาเหตุก็เพราะว่า ตรงนี้เรามีการประกาศ object ตรงจังหวะนี้เลย โดยภายใน object นี้จะมี attribute สองตัว กล่าวคือ height, width ซึ่งใน javascript ยอมให้เราประกาศ object ตรงจังหวะใช้งานเลยก็ได้ (ต่างกับกรณี USER_DATA ที่มีการประกาศไว้ก่อน ณ ส่วนบนสุดของ index.js ดังนั้นเวลาจะนำมาใช้ จึงสามารถอ้างเป็น {USER_DATA} ได้เลย)

ลองเข้าไปที่ browser แล้ว refresh ดู หากไม่มีอะไรผิดพลาด จะเห็นภาพดังนี้

screen-shot-2559-11-01-at-4-41-32-pm

หมายเหตุ หากรันโปรแกรมตามแล้วมีข้อผิดพลาด และหาสาเหตุของปัญหาไม่เจอ ก็สามารถเข้าไปดู code ต้นแบบได้ที่

https://github.com/himaeng/react-fun-course/tree/master/02-pure-function