Babel Coder

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

advanced

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

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

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

สารบัญ

รู้จัก Optional Chaining

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

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

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

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

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

article.author?.name

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

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

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

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

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

const res = await fetch('/token')
let val

switch(res.status) {
  case 200:
  	val = res.headers.get('Authorization')
  	break
  case 401:
  	val = 'Unauthorized'
  	break
  default:
  	if (res.status >= 400) throw new RequestError(res)
}

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

const res = await fetch('/token')
const val = match(res) {
	{ status: 200, headers } => headers.get('Authorization')
	{ status: 401 } => 'Unauthorized'
	{ status } if (status >= 400) => throw new RequestError(res)
}

เราทราบว่าอ็อบเจ็กต์ res มีพร็อพเพอร์ตี้ status อยู่ภายใน อาศัยความจริงนี้เราจึงสามารถเข้าคู่ status ที่มีค่าเสมอกันเข้าด้วยกันได้ หาก status นั้นมีค่าเป็น 200 headers.get('Authorization') จึงถูกทำงาน

เราสามารถนำหลักการ Pattern Matching นี้มาใช้คู่กับ Reducer ของ Redux ได้ จากเดิมที่เราอาจเขียน Reducer เช่นนี้

const TodoApp = (state = initialState, action) => {
	switch(action.type) {
		case SET_VISIBILITY_FILTER:
			return { filter: action.filter }
		case ADD_TODO:
			return [...state.todos, { text: action.text }]
		default:
			return state
	}
}

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

const TodoApp = (state = initialState, action) => match (action) {
	{ type: SET_VISIBILITY_FILTER, filter } => ({ filter })
	{ type: ADD_TODO } => [...state.todos, { text }]
	{} => state
}

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

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

function isNumber(obj, key) {
	// ...
	return obj
}

function isEmail(obj, key) {
	// ...
	return obj
}

const person = { age: 20, email: 'prayuuuuud@m44.com' }

let obj = isNumber(person, 'age')
obj = isEmail(person, 'email')

// หรือ
isEmail(isNumber(person, 'age'), 'email')

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

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

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

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

class Profile extends Component {
	// ...
}

export default withRouter(withAuth(Profile))

// หรือ
export default compose(withRouter, withAuth)(Profile)

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

export default Profile |> withAuth |> withRouter

เท่ชะมัด!

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

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

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

<Box>
    {
      shouldShowAnswer(user) ? (
      		<Answer value={false}>no</Answer>
      ) : (
	      <Box.Comment>
	         Text Content
	      </Box.Comment>
	   )
    }
</Box>

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

<Box>
    {
      select (shouldShowAnswer(user)) {
        ::when (true) {
          <Answer value={false}>no</Answer>
        }
        ::when (false) {
          <Box.Comment>
             Text Content
          </Box.Comment>
        }
      }
    }
</Box>

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

<Box>
    {
      do {
        if (shouldShowAnswer(user)) {
          <Answer value={false}>no</Answer>
        }
        else {
          <Box.Comment>
             Text Content
          </Box.Comment>
        }
      }
    }
</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


แสดงความคิดเห็นของคุณ


JungKo5 เดือนที่ผ่านมา

ขอบคุณครับ