สร้างแอป Twitter แบบง่ายๆ ด้วย React และ Recompose
สร้างแอป Twitter แบบง่ายๆ
ในบทความนี้ผมจะแสดงการนำ Recompose มาใช้ร่วมกับ React ในการสร้างแอป twitter แบบง่ายๆ ให้ดูนะครับ โดยผมจะพยายามแสดงให้เห็นประโยชน์ของ Recompose กับ React เมื่อมาใช้ในการสร้างแอปจริงๆ
สำหรับคนที่ไม่เข้าใจ Function Composition กับ Higher-order Component ผมแนะนำให้อ่านบทความนี้ก่อนนะครับ พื้นฐานก่อนศึกษา Recompose สำหรับ React
โดยแอป Twitter ของเราก็จะมีหน้าตา mockup ประมาณนี้
โดยแอปนี้จะมีสเปคคร่าวๆ ตามนี้
- สามารถพิมพ์ข้อความและทวีตลงไปได้
- มีการจำกัดตัวอักษรไว้ที่ 140 ตัวอักษรและแสดงจำนวนตัวอักษรที่เหลือข้างๆ ปุ่ม “Tweet”
- ถ้าไม่ได้พิมพ์อะไรเลยหรือพิมพ์ตัวอักษรเกินกว่าที่กำหนดปุ่ม “Tweet” จะถูก disable ไว้
- ถ้าจำนวนตัวอักษรที่เหลือติดลบสีจะเปลื่ยนเป็นสีแดง
เริ่มแรกให้ทำการติดตั้ง React ก่อนเลยนะครับจะติดตั้งผ่าน create-react-app หรือจะ set up ขึ้นมาเองก็ได้ โดยสร้างโครงโปรเจคประมาณนี้ก่อนนะครับ
จากโค้ดด้านบนก็ไม่มีอะไรมากนะครับเพียงแค่มี TweetHome
เป็น component ที่ไว้แสดง TweetBox
ไว้ด้านบนและ Tweets
แสดงด้านล่าง (Gist อาจจะดูยากหน่อยนะครับ ใจจริงอยากให้ TweetHome.js
อยู่ข้างบนสุด)
เราก็จะได้หน้าตาแอปแบบนี้ออกมานะครับ
ต่อไปเราจะนำเอา Recompose พระเอกของงาน มาใช้งานสักทีนะครับ วิธีติดตั้งก็เพียงแค่พิมพ์คำสั่ง npm install --save recompose
หรือ yarn add recompose
ขั้นตอนต่อไปเราจะสร้างส่วนของกล่องทวีตนะครับ (ส่วนด้านบนของรูป mockup)
จากโค้ดข้างบนนะครับที่ไฟล์ TweetHome.js
บรรทัดที่ 14 เราจะใช้ฟังก์ชั่น compose
ของ Recompose มาใช้ในการประกอบหลายๆ High-order component (HOC) เข้าด้วยกันแล้วนำไปเก็บไว้ในตัวแปร enhance
ชึ่งข้างในประกอบไปด้วยการกำหนด state ของ component นี้มาสามตัว limit
user
และ message
โดยใช้ฟังก์ชัน withState
ฟังก์ชัน withState
รับ parameter เข้าไป 3 parameter ตัวแรกเป็นการกำหนดชื่อของ state ตัวต่อมาเป็นการกำหนดชื่อของฟังก์ชันที่ใช้ในการอัปเดตค่าของ state และตัวสุดท้ายเป็นการกำหนดค่าเริ่มต้นของ state นั้น
และทำการกำหนด handler onMessageChange
โดยใช้ฟังก์ชัน withHandlers
ฟังก์ชันนี้จะรับ Object โดยแต่ละ property ของ Object จะต้องเป็น higher-order functions ที่รับ props และรีเทิร์น ฟังก์ชัน handle ออกไป
จากฟังก์ชัน withHandlers
บางคนอาจจะสงสัยว่าทำไมต้องใช้ฟังก์ชัน withHandlers
ด้วย ทำไมไม่ใช้ mapProps
หรือ withProps
ชึ่งก็ใช้ได้เหมือน ลองมาดูตัวอย่างข้างล่างนี้ดูนะครับ
จากโค้ดข้างบนทั้ง enhance
และ enhance2
ทำงานได้เหมือนกันทั้งคู่นะครับแต่จะต่างกันตรงที่ว่า onMessageChange
ของ enhance2
จะถูกสร้างใหม่ทุกครั้งใหม่ทุกครั้งที่มีการอัปเดต state หรือ props ผมจึงแนะนำว่าเวลาที่จะทำฟังก์ชัน handle ตัว Event ต่างๆ ให้ใช้ withHandler
ดีกว่านะครับจะช่วยเรื่อง Performance ของแอปได้ระดับนึง อ่านต่อเพิ่มเติมได้ที่นี้เลย recompose/withHandler
มาต่อที่ TweetHome.js
ดูที่บรรทัดที่ 23 ตัวแปร TweetHome
มันเป็นเพียงแค่ ฟังก์ชันที่รับ props เข้าไปและรีเทิร์น JSX ออกมา และนี้คือสิ่งที่ผมชอบมากๆ ของ Recompose มันทำให้ผมไม่ต้องมาปวดหัวกับการใช้ class this bind คือมันทำให้ผมลืมเรื่อง class ใน Javascript ไปได้เลย
สิ่งที่ผมทำหรือคิดเวลาสร้างแต่ละ component ผมเพียงแค่คิดว่าหน้าตาของ component เป็นแบบไหนต้องใช้ props หรือ state อะไรบ้างโดยไม่จำเป็นต้องไปกังวลเรื่องที่ว่า props หรือ state นั้นๆ จะมาจากไหน เปลื่ยนยังไง เอาไว้ที่ไหน ส่วนเรื่องจัดการ props หรือ state ก็แยกไปทำโดยใช้ Recompose แทน หมดปัญหาเรื่องที่ต้องมาคิดทั้งการแสดงและการจัดการ state ในเวลาเดียวกันได้เลยครับ (จริงๆ อาจจะไม่ใช่ปัญหาก็ได้นะครับการคิดเรื่องแสดงผลกับจัดการ state ในเวลาเดียวกัน อาจจะดีกว่าด้วยซ้ำ แต่ว่าผมชอบแบบนี้มากกว่า)
มาต่อที่ไฟล์ TweetBox.js
ไฟล์นี้เราก็จะทำการนำ props ที่ได้จาก TweetHome
มาคำนวนค่าต่างๆ ที่จำเป็นต้องใช้ใน TweetBox
โดยใช้ฟังก์ชัน mapProps
จะมีตัวที่น่าสนใจคือฟังก์ชัน flattenProp
ฟังก์ชันนี้มีหน้าที่คล้ายเครื่องหมาย … (Spread) ของ Javascript คือมันจะแยก property ต่างๆ ของ Object ที่กำหนด ออกไปเป็น props ของ Base Component
ตอนนี้แอปจะหน้าตาประมาณนี้นะครับ
โดยยังไม่สามารถทวีตและแสดงได้
ขั้นตอนสุดท้ายเราจะทำให้แอปนี้สมบูรณ์ตามสเปคที่เราเขียนไว้เมื่อตอนต้นบทความนะครับ
จากโค้ดที่เพิ่มเข้ามาในไฟล์ Tweet.js
ก็ทำการแสดงข้อมูลแต่ละทวีตโดยใน component นี้มีการใช้ฟังก์ชัน onlyUpdateForKeys
เป็นฟังก์ชันที่รับ Array ที่เก็บ key ที่เราต้องการตรวจสอบเข้าไป หากมี props อันใดอันหนึ่งที่กำหนดไว้มีการเปลื่ยนแปลงขึ้นมา component ก็จะ render ใหม่ สะดวกใช่ไหมล่ะครับ ไม่ต้องมานั่งเขียน shouldComponentUpdate()
เองให้เหนื่อย
หน้าตาแอปตอนนี้ก็จะได้ดังรูปด้านล่างครับ
สรุป
ก็อย่างที่เห็นครับ Recompose ช่วยให้โค้ดดูอ่านง่ายสะอาดตามาก ทำให้ผมสามารถแยกการพัฒนาระหว่างการจัดการ State ใน Component กับการแสดงผลได้ พวก High-order Component ต่างก็สามารถ Test ได้ง่ายๆแน่นอนครับยิ่ง Component ธรรมดา ยิ่งง่ายใหญ่ ไม่การคำนวนหรือ Tranfrom ข้อมูลเลย มีเพียงแต่การแสดงผล และอีกอย่างถ้าแอปที่มีขนาดใหญ่หรือซับซ้อนมากกว่านี้บวกกับเราออกแบบมันดีพอ พวก High-order Component ที่สร้างขึ้นก็จะสามารถ Reusable
ผมได้ทำแอปแบบเดียวกันแต่ว่าเขียนอยู่ในรูปแบบปกติที่ไม่ได้ใช้ Recompose ใน Github ของผมไปดูกันได้เลยแล้วลองดูซิว่าชอบแบบไหนมากกว่ากัน https://github.com/dumpsayamrat/tweet-box
Recompose ยังมี API ที่เจ๋งๆ อีกเยอะนะครับไม่ว่าจะเป็น branch, onlyUpdateForPropTypes, lifecycle
ลองตามไปดูกันได้