ปี 2020 โค๊ด React เราจะเปลี่ยนแปลงยังไง?
สวัสดีปี 2018 พวกเธอว์ยังแฮปปี้แลชีวิตดี๊ดีกับการเขียนโค๊ด React อยู่หรือไม่?
แรกสัมผัสความ React ย่อมหอมละมุน คบไปซักพักเริ่มกลายเป็นวิถีแห่งความเวิ่นเว้อ จะทำอะไรนิดปรับแต่งอะไรหน่อยนี่ต้องเขียนโค้ดกันยาวตั้งแต่กรุงเทพไปสุไหงโกรก!
React ไม่ใช่แพะหากแต่เป็น JavaScript ที่เราต้องเขียนโค้ดตามหลักภาษาของมัน บางส่วนที่สายย่อมักคิดว่าย่อได้กลับทำไม่ได้ใน JavaScript แต่นั่นกำลังจะเป็นความหลังเพราะ JavaScript ในโลกยุคใหม่อันใกล้กำลังจะเพิ่มฟีเจอร์ใหม่ ๆ เหล่านี้ และนั่นจึงเป็นจุดเริ่มต้นของโค้ด React ที่ดูกระชับขึ้นแน่นอน # แจ็คแม้วอยู่เบื้องหลังชัวร์
รู้จัก Optional Chaining
ในสถานการณ์ที่เราต้องแสดงผลชื่อผู้แต่งบทความโดยส่วนของชื่ออยู่ใต้อ็อบเจ็กต์ author ความยากมีแค่ระดับศูนย์
1<div>{article.author.name}</div>
ทว่าโชคช่างไม่เข้าข้าง คราใดที่อ็อบเจ็กต์ author นั้นว่างเปล่าเป็น undefined การเรียก name ออกมานั้นย่อมทำไม่ได้ ด้วยคุณสมบัติของ &&
มาตรการของเราจึงเป็นการตรวจสอบก่อนว่า author มีอยู่จริงหรือไม่ถ้ามีจึงทำต่อ
1<div>{article.author && article.author.name}</div>
เกือบดีละ แต่จะดีกว่านี้หากเราไม่ต้องเขียน article.author
ซ้ำกันถึง 2 ที่! เหล่าพลเมือง React เอ๋ยจงอิจฉาเพราะ Angular 2+ มีสิ่งที่เรียกว่า Safe navigation operator (?.) ไว้ช่วยชีวิต
1article.author?.name
การใส่ ?. เป็นการย้ำเตือนว่าหากอ็อบเจ็กต์ author นั้นเป็น null หรือ undefined ก็จงอย่าเสนอหน้ามาเรียก name จาก author เพราะจะเกิดข้อผิดพลาดไปซะเปล่า ๆ แต่เมื่อใดที่ author มีค่าจึงทำการเรียก name ออกมาอีกที ฉลาดใช่ไหมหละ
ECMAScript ภาษาต้นน้ำของ JavaScript ได้ปล่อยมาตรฐานที่เรียกว่า Optional Chaining
(Stage-1 ขณะเขียนบทความ) ที่มีลักษณะการใช้งานเหมือน Safe navigation operator ของเทมเพลตใน Angular2+ เป๊ะ เมื่อมาตรฐานนี้ได้รับการใช้งานเราจึงคาดหวังว่าโค้ดต่อไปนี้ของ React จะสมบูรณ์
1<div>{article.author?.name}</div>
Reducer ที่เป็นฟังก์ชันนอลมากขึ้นด้วย Pattern Matching
สมมติเราต้องการส่งการร้องขอ token ของผู้ใช้งานระบบที่ /token
เป็นไปได้ว่าเซิฟเวอร์จะตอบกลับมาด้วย 200 OK หรือ 401 Unauthorized เป็นต้น เราจึงต้องเขียนโค้ดเพื่อควมคุมพฤติกรรมดังกล่าว
1const res = await fetch('/token')2let val34switch (res.status) {5 case 200:6 val = res.headers.get('Authorization')7 break8 case 401:9 val = 'Unauthorized'10 break11 default:12 if (res.status >= 400) throw new RequestError(res)13}
สมเหตุสมผลและไม่ได้ผิดอะไร แต่เมื่อความเป็น Funtional Programing มันซึมเข้ากระแสเลือดแล้ว ชีวิตเราคงต้องการเกลือแร่ที่ชื่อ Pattern Matching (Stage-0 ขณะเขียนบทความ) ที่จะช่วยลดโค้ดของเราให้เหลือสั้นเท่าจู๋มด ดังนี้
1const res = await fetch('/token')2const val = match(res) {3 { status: 200, headers } => headers.get('Authorization')4 { status: 401 } => 'Unauthorized'5 { status } if (status >= 400) => throw new RequestError(res)6}
เราทราบว่าอ็อบเจ็กต์ res มีพร็อพเพอร์ตี้ status อยู่ภายใน อาศัยความจริงนี้เราจึงสามารถเข้าคู่ status ที่มีค่าเสมอกันเข้าด้วยกันได้ หาก status นั้นมีค่าเป็น 200 headers.get('Authorization')
จึงถูกทำงาน
เราสามารถนำหลักการ Pattern Matching นี้มาใช้คู่กับ Reducer ของ Redux ได้ จากเดิมที่เราอาจเขียน Reducer เช่นนี้
1const TodoApp = (state = initialState, action) => {2 switch (action.type) {3 case SET_VISIBILITY_FILTER:4 return { filter: action.filter }5 case ADD_TODO:6 return [...state.todos, { text: action.text }]7 default:8 return state9 }10}
แปลงโฉมใหม่ด้วย Pattern Matching ได้ว่า
1const TodoApp = (state = initialState, action) => match (action) {2 { type: SET_VISIBILITY_FILTER, filter } => ({ filter })3 { type: ADD_TODO } => [...state.todos, { text }]4 {} => state5}
สง่างามกว่าด้วย Pipeline Operator
กรณีของการตรวจสอบข้อมูลที่ต้องส่งอ็อบเจ็กต์ไปตรวจสอบในหลาย ๆ ฟังก์ชัน โค้ดเราอาจเป็นเช่นนี้
1function isNumber(obj, key) {2 // ...3 return obj4}56function isEmail(obj, key) {7 // ...8 return obj9}1011const person = { age: 20, email: 'prayuuuuud@m44.com' }1213let obj = isNumber(person, 'age')14obj = isEmail(person, 'email')1516// หรือ17isEmail(isNumber(person, 'age'), 'email')
ไม่ว่าแบบไหนก็หน่อมแน้มไปหน่อย คุณสมบัติของ Pipeline Operator จะทำให้เราสามารถส่งผลลัพธ์ก่อนหน้าไปเป็น input ของการเรียกครั้งถัดไปได้ จึงเกิดพฤติกรรมของการเรียกแบบต่อเนื่องได้ดังนี้
1person |> isNumber('age') |> isEmail('email')
การมาของ Pipeline Operator ยิ่งทำให้ชีวิตเดฟ React น้อย ๆ สะดวกขึ้น
สมมติเรามีคอมโพแนนท์ Profile เราคาดหวังว่าผู้ใช้งานต้องผ่านการยืนยันตัวตนก่อนเราจึงสร้าง withAuth เป็น Higher-Order Component ขึ้นมาครอบ พร้อมกับครอบทับอีกชั้นด้วย withRouter เพื่อให้มีความสามารถในการจัดการเส้นทาง ดังนี้
1class Profile extends Component {2 // ...3}45export default withRouter(withAuth(Profile))67// หรือ8export default compose(withRouter, withAuth)(Profile)
หากเมื่อใช้ Pipeline Operator โค้ดของเราก็จะดูกรุบกริบขึ้น แบบนี้
1export default Profile |> withAuth |> withRouter
เท่ชะมัด!
Pipeline Operator นี่น่าสนเนอะ แต่จะมาชาติไหนก็มิอาจทราบได้เพราะขณะที่เขียนบทความมันยังไม่ Stage-0 เลย สงสัยจะได้เลือกตั้งก่อน~
ทำให้ชัดเจนขึ้นด้วย do expressions และ block params
สถานการณ์ที่ต้องเลือกแสดงผลอย่างใดอย่างหนึ่งบน React เรามักนิยมใช้ Ternary Operator
1<Box>2 {shouldShowAnswer(user) ? (3 <Answer value={false}>no</Answer>4 ) : (5 <Box.Comment>Text Content</Box.Comment>6 )}7</Box>
แต่นั่นก็อาจสร้างความสับสนให้ใครหลายคน แล้วถ้าเราเลือกประกาศให้ชัดเจนลงไปเลยละ ว่าส่วนนี้ทำงานเมื่อเงื่อนไขเป็นจริงและอีกส่วนทำงานเมื่อเงื่อนไขเป็นเท็จ
1<Box>2 {3 select (shouldShowAnswer(user)) {4 ::when (true) {5 <Answer value={false}>no</Answer>6 }7 ::when (false) {8 <Box.Comment>9 Text Content10 </Box.Comment>11 }12 }13 }14</Box>
นี่คือหนึ่งในรูปแบบของสิ่งที่เรียกว่า block params แต่ถ้าคุณว่าสิ่งนี้ดูยากไปเรามีอีกไวยากรณ์ของ do expressions ที่ทำให้โค้ดนี้ชัดเจนมากขึ้นได้เช่นกัน
1<Box>2 {do {3 if (shouldShowAnswer(user)) {4 ;<Answer value={false}>no</Answer>5 } else {6 ;<Box.Comment>Text Content</Box.Comment>7 }8 }}9</Box>
เราทราบกันดีว่าภายใต้ jsx เราไม่สามารถใช้ if-else statement ได้ แต่การใช้ do expressions ครอบทับสามารถเกิดขึ้นได้เพราะมันเป็น expression นั่นเอง
ชั่วโมงขายของ
Babel Coder มีคอร์สสอนสด React ด้วยนะ! 9 - 10 มิย 2561
ครอบคลุมเนื้อหาทั้ง ES6+, React, Redux, React Router และอื่น ๆ
รายละเอียดเพิ่มเติม: คลิกดูที่นี่ฮะ
สนใจติดต่อเพจ BabelCoder ครับ
เอกสารอ้างอิง
Block Params. Retrieved April, 29, 2018, from https://github.com/samuelgoto/proposal-block-params
ECMAScript Pattern Matching. Retrieved April, 29, 2018, from https://github.com/tc39/proposal-pattern-matching
ESNext Proposal: The Pipeline Operator. Retrieved April, 29, 2018, from https://github.com/tc39/proposal-pipeline-operator
ECMAScript proposal: do expressions. Retrieved April, 29, 2018, from https://github.com/tc39/proposal-do-expressions
Optional Chaining for JavaScript. Retrieved April, 29, 2018, from https://github.com/tc39/proposal-optional-chaining
สารบัญ
- รู้จัก Optional Chaining
- Reducer ที่เป็นฟังก์ชันนอลมากขึ้นด้วย Pattern Matching
- สง่างามกว่าด้วย Pipeline Operator
- ทำให้ชัดเจนขึ้นด้วย do expressions และ block params
- ชั่วโมงขายของ
- เอกสารอ้างอิง