ปี 2020 โค๊ด React เราจะเปลี่ยนแปลงยังไง?

Nuttavut Thongjor

สวัสดีปี 2018 พวกเธอว์ยังแฮปปี้แลชีวิตดี๊ดีกับการเขียนโค๊ด React อยู่หรือไม่?

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

React ไม่ใช่แพะหากแต่เป็น JavaScript ที่เราต้องเขียนโค้ดตามหลักภาษาของมัน บางส่วนที่สายย่อมักคิดว่าย่อได้กลับทำไม่ได้ใน JavaScript แต่นั่นกำลังจะเป็นความหลังเพราะ JavaScript ในโลกยุคใหม่อันใกล้กำลังจะเพิ่มฟีเจอร์ใหม่ ๆ เหล่านี้ และนั่นจึงเป็นจุดเริ่มต้นของโค้ด React ที่ดูกระชับขึ้นแน่นอน # แจ็คแม้วอยู่เบื้องหลังชัวร์

รู้จัก Optional Chaining

ในสถานการณ์ที่เราต้องแสดงผลชื่อผู้แต่งบทความโดยส่วนของชื่ออยู่ใต้อ็อบเจ็กต์ author ความยากมีแค่ระดับศูนย์

JSX
1<div>{article.author.name}</div>

ทว่าโชคช่างไม่เข้าข้าง คราใดที่อ็อบเจ็กต์ author นั้นว่างเปล่าเป็น undefined การเรียก name ออกมานั้นย่อมทำไม่ได้ ด้วยคุณสมบัติของ && มาตรการของเราจึงเป็นการตรวจสอบก่อนว่า author มีอยู่จริงหรือไม่ถ้ามีจึงทำต่อ

JSX
1<div>{article.author && article.author.name}</div>

เกือบดีละ แต่จะดีกว่านี้หากเราไม่ต้องเขียน article.author ซ้ำกันถึง 2 ที่! เหล่าพลเมือง React เอ๋ยจงอิจฉาเพราะ Angular 2+ มีสิ่งที่เรียกว่า Safe navigation operator (?.) ไว้ช่วยชีวิต

JavaScript
1article.author?.name

การใส่ ?. เป็นการย้ำเตือนว่าหากอ็อบเจ็กต์ author นั้นเป็น null หรือ undefined ก็จงอย่าเสนอหน้ามาเรียก name จาก author เพราะจะเกิดข้อผิดพลาดไปซะเปล่า ๆ แต่เมื่อใดที่ author มีค่าจึงทำการเรียก name ออกมาอีกที ฉลาดใช่ไหมหละ

ECMAScript ภาษาต้นน้ำของ JavaScript ได้ปล่อยมาตรฐานที่เรียกว่า Optional Chaining (Stage-1 ขณะเขียนบทความ) ที่มีลักษณะการใช้งานเหมือน Safe navigation operator ของเทมเพลตใน Angular2+ เป๊ะ เมื่อมาตรฐานนี้ได้รับการใช้งานเราจึงคาดหวังว่าโค้ดต่อไปนี้ของ React จะสมบูรณ์

JSX
1<div>{article.author?.name}</div>

Reducer ที่เป็นฟังก์ชันนอลมากขึ้นด้วย Pattern Matching

สมมติเราต้องการส่งการร้องขอ token ของผู้ใช้งานระบบที่ /token เป็นไปได้ว่าเซิฟเวอร์จะตอบกลับมาด้วย 200 OK หรือ 401 Unauthorized เป็นต้น เราจึงต้องเขียนโค้ดเพื่อควมคุมพฤติกรรมดังกล่าว

JavaScript
1const res = await fetch('/token')
2let val
3
4switch (res.status) {
5 case 200:
6 val = res.headers.get('Authorization')
7 break
8 case 401:
9 val = 'Unauthorized'
10 break
11 default:
12 if (res.status >= 400) throw new RequestError(res)
13}

สมเหตุสมผลและไม่ได้ผิดอะไร แต่เมื่อความเป็น Funtional Programing มันซึมเข้ากระแสเลือดแล้ว ชีวิตเราคงต้องการเกลือแร่ที่ชื่อ Pattern Matching (Stage-0 ขณะเขียนบทความ) ที่จะช่วยลดโค้ดของเราให้เหลือสั้นเท่าจู๋มด ดังนี้

JavaScript
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 เช่นนี้

JavaScript
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 state
9 }
10}

แปลงโฉมใหม่ด้วย Pattern Matching ได้ว่า

JavaScript
1const TodoApp = (state = initialState, action) => match (action) {
2 { type: SET_VISIBILITY_FILTER, filter } => ({ filter })
3 { type: ADD_TODO } => [...state.todos, { text }]
4 {} => state
5}

สง่างามกว่าด้วย Pipeline Operator

กรณีของการตรวจสอบข้อมูลที่ต้องส่งอ็อบเจ็กต์ไปตรวจสอบในหลาย ๆ ฟังก์ชัน โค้ดเราอาจเป็นเช่นนี้

JavaScript
1function isNumber(obj, key) {
2 // ...
3 return obj
4}
5
6function isEmail(obj, key) {
7 // ...
8 return obj
9}
10
11const person = { age: 20, email: 'prayuuuuud@m44.com' }
12
13let obj = isNumber(person, 'age')
14obj = isEmail(person, 'email')
15
16// หรือ
17isEmail(isNumber(person, 'age'), 'email')

ไม่ว่าแบบไหนก็หน่อมแน้มไปหน่อย คุณสมบัติของ Pipeline Operator จะทำให้เราสามารถส่งผลลัพธ์ก่อนหน้าไปเป็น input ของการเรียกครั้งถัดไปได้ จึงเกิดพฤติกรรมของการเรียกแบบต่อเนื่องได้ดังนี้

JavaScript
1person |> isNumber('age') |> isEmail('email')

การมาของ Pipeline Operator ยิ่งทำให้ชีวิตเดฟ React น้อย ๆ สะดวกขึ้น

สมมติเรามีคอมโพแนนท์ Profile เราคาดหวังว่าผู้ใช้งานต้องผ่านการยืนยันตัวตนก่อนเราจึงสร้าง withAuth เป็น Higher-Order Component ขึ้นมาครอบ พร้อมกับครอบทับอีกชั้นด้วย withRouter เพื่อให้มีความสามารถในการจัดการเส้นทาง ดังนี้

JavaScript
1class Profile extends Component {
2 // ...
3}
4
5export default withRouter(withAuth(Profile))
6
7// หรือ
8export default compose(withRouter, withAuth)(Profile)

หากเมื่อใช้ Pipeline Operator โค้ดของเราก็จะดูกรุบกริบขึ้น แบบนี้

JavaScript
1export default Profile |> withAuth |> withRouter

เท่ชะมัด!

Pipeline Operator นี่น่าสนเนอะ แต่จะมาชาติไหนก็มิอาจทราบได้เพราะขณะที่เขียนบทความมันยังไม่ Stage-0 เลย สงสัยจะได้เลือกตั้งก่อน~

ทำให้ชัดเจนขึ้นด้วย do expressions และ block params

สถานการณ์ที่ต้องเลือกแสดงผลอย่างใดอย่างหนึ่งบน React เรามักนิยมใช้ Ternary Operator

JSX
1<Box>
2 {shouldShowAnswer(user) ? (
3 <Answer value={false}>no</Answer>
4 ) : (
5 <Box.Comment>Text Content</Box.Comment>
6 )}
7</Box>

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

JSX
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 Content
10 </Box.Comment>
11 }
12 }
13 }
14</Box>

นี่คือหนึ่งในรูปแบบของสิ่งที่เรียกว่า block params แต่ถ้าคุณว่าสิ่งนี้ดูยากไปเรามีอีกไวยากรณ์ของ do expressions ที่ทำให้โค้ดนี้ชัดเจนมากขึ้นได้เช่นกัน

JSX
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 ครับ

React Fundamentals

เอกสารอ้างอิง

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
  • ชั่วโมงขายของ
  • เอกสารอ้างอิง