เพิ่มความสามารถให้กับ Functional Component ด้วย React Hooks จาก React 16.7.0

ช่วงนี้ดูเหมือนทีมงาน React จะขยันเป็นพิเศษ เพราะมีปล่อยอัพเดทพร้อมฟีเจอร์ใหม่ออกมาให้เราได้ลองใช้ลองเล่นกันแบบรัวๆ โดยเวอร์ชั่นล่าสุดที่ปล่อยออกมาก็คือ v16.7.0 ที่ตอนนี้ยังเป็นตัว alpha อยู่ ซึ่งเวอร์ชั่นนี้มีฟีเจอร์ที่น่าสนใจมากๆ ก็คือ Hooks วันนี้ก็เลยจะมาพูดเกี่ยวกับเจ้าฟีเจอร์ Hooks นี้ซะหน่อย

Class vs Functional Components

การเขียน component นั้น เราสามารถเขียนได้ 2 แบบ ด้วยกันคือ class component ซึ่งเป็น component ที่มีการเก็บ state และสามารถเรียกใช้งาน lifecycle hooks ต่างๆ ได้ และอีกแบบหนึ่งคือ functional component ที่รับ props จาก class component เพื่อมาแสดงค่าอีกที

แต่ว่าบางทีเราอยากให้ functional component ของเรามีการถือ state หรือ lifecycle เพื่อทำให้การเขียน component ของเรามีความสามารถในการ reusable มากขึ้น ซึ่ง React Hooks ก็เข้ามาตอบโจทย์ตรงนี้ เพราะสามารถเพิ่ม state และ lifecycle เข้าไปใน functional component ของเราได้เลย

เพิ่ม State ใน Functional Component ด้วย useState

ปกติแล้วการสร้าง state และอัพเดท state ใน class component จะมีหน้าตาประมาณนี้

State และวิธีการอัพเดท state ใน class component

แต่ถ้าเราอยากจะเพิ่ม state หรืออัพเดท state ให้กับ functional component เราก็สามารถทำได้ง่ายๆ ด้วย useState

เพิ่ม state ใน functional component ด้วย useState

สิ่งที่เราต้องส่งเข้าไปใน useState คือ initialState ซึ่งเป็นค่าเริ่มต้น ในที่นี้คือ 0 ส่วนสิ่งที่ส่งคืนกลับมานั้นจะอยู่ในรูปของ array destructuring โดยค่าภายใน array ก็คือ

  • state ปัจจุบันของ component
  • function สำหรับการอัพเดท state นั้นๆ

หรือถ้าเราอยากจะให้ state ใหม่นั้นเปลี่ยนแปลงตาม state ก่อนหน้า ก็สามารถส่ง state ก่อนหน้าเข้าไปยัง function setState ได้เหมือนกัน

useState สามารถใช้งาน state เก่าได้

นอกจากนี้ ถ้า component ต้องการใช้งานหลายๆ state ก็เพียงแค่ประกาศตัวแปรเพิ่มตามต้องการ

ตัวอย่างการใช้งาน useState มากกว่าหนึ่งตัวใน component

การอัพเดท state ด้วย useState แตกต่างจากกับการเรียกใช้ this.setState ตรงที่ state ไม่ใช่การ merge ระหว่าง state ใหม่กับ state ก่อนหน้า แต่ว่าเป็นการทับของเก่าเลย

เพิ่ม Lifecycle Hooks ใน Functional Component ด้วย useEffect

ได้เห็นวิธีการเพิ่ม state ใน functional component กันไปแล้ว มาดูวิธีการเพิ่ม lifecycle hooks กันบ้างดีกว่า โดยปกติแล้วการทำพวก side effect ต่างๆ อย่างเช่น การ fetch ข้อมูลจากข้างนอก หรือการอัพเดท DOM นั้น จะถูกทำผ่าน componentDidMount หรือ componentDidUpdate แต่ Hooks ทำให้เราสามารถทำกระบวนการ side effect ใน functional component ได้เลย ผ่าน function useEffect

Lifecycle hooks ใน class component

useEffect เป็นเหมือนการนำ componentDidMount, componentDidUpdate และ componentWillUnmount มารวมกันเป็น function เดียว เราลองมาดูกันดีกว่าว่า useEffect เนี่ยะ ใช้งานยังไงกันแน่

เพิ่ม lifecycle hooks ใน functional component ด้วย useEffect

ทุกครั้งที่ component มีการ render หรือมีการอัพเดท useEffect จะถูกเรียก โดยสิ่งที่ต้องส่งเข้าไปให้กับ useEffect ก็จะมี callback function เป็น parameter แรก เพื่อทำกระบวนการ side effect ต่างๆ อารมณ์เดียวกับ componentDidMount

ส่วน parameter ที่สองรับเข้าไปคือ array โดยถ้าหากว่าเราไม่ได้ใส่ parameter ที่สองเข้าไป useEffect จะถูกเรียกแบบรัวๆ เพราะว่าเมื่อเราไป fetch data มา component จะมีการอัพเดท state ทำให้ component มีการ re-render ก็เลยไปสั่งให้ useEffect ให้ทำงานอีกครั้ง เราก็เลยต้องอย่างน้อยใส่ array ว่างเข้าไป เพื่อให้ทำงานเฉพาะตอน component นั้น mount หรือ unmount เพียงอย่างเดียว

สำหรับ callback ตัวที่สองนั้นทำหน้าที่คล้ายๆ กับ componentWillUnmount ซึ่งหน้าที่ของมันคือการเคลียร์สิ่งที่รันก่อนหน้า อย่างในที่นี้คือเคลียร์การ fetch data ก่อนที่จะมีการ re-render

useEffect แบบ mount/unmount อย่างเดียว

วิธีการแก้ปัญหาก็คือ เพิ่ม array เข้าไปเป็น parameter ซึ่งถ้าหากว่าเป็น array ว่าง useEffect จะทำงานตอนที่ component นั้นมีการ mount หรือ unmount เพียงอย่างเดียว

เช็คค่า repo ว่ามีการเปลี่ยนแปลงหรือไม่

แต่ถ้าหากว่าเราอยากให้ useEffect นั้นทำงานแบบ componentDidUpdate ก็ต้องใส่ค่าเข้าไปใน array เพื่อให้ useEffect รู้ว่าจะเรียกใช้งานเมื่อค่าใน array นั้นมีการเปลี่ยนแปลง ในตัวอย่างข้างบนจะเช็คว่า this.props.repo ที่ส่งเข้ามาจาก parent component มีการเปลี่ยนแปลงมั้ยถ้าไม่เปลี่ยนก็ไม่ต้องเรียก useEffect อีกรอบ

React Hooks ไม่ได้มีแค่ useState กับ useEffect

ที่จริงแล้ว React Hooks นั้นมีมากกว่าแค่ useState กับ useEffect แต่ในบล็อกนี้ขอเขียนแค่สองตัวนี้ก่อน เพราะว่าเป็น function ที่น่าจะได้ใช้เยอะที่สุด ส่วนตัวอื่นๆ ก็จะมี

  • useContext
  • useReducer
  • useCallback
  • useMemo
  • useRef
  • useImperativeMethods
  • useMutationEffect
  • useLayoutEffect

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

จะลองเล่น React Hooks ยังไง

ตอนนี้ React Hooks มาพร้อมกับ React v16.7.0 alpha ซึ่งถ้าใครอยากจะลองใช้ก่อนก็ต้องลงผ่าน yarn add react@16.7.0-alpha.0 หรือ npm install — save react@16.7.0-alpha.0 ก็สามารถเล่นได้เลยยย

สรุป

การมาของ React Hooks ทำให้การเขียน React นั้นทำให้ component ของเราสามารถทำ reuse ได้ง่ายขึ้น เพราะเราสามารถที่จะแชร์ logic ระหว่าง functional component ได้เลย แถมยังให้เขียนโค้ดได้สั้นลงด้วย แทนที่เราจะเขียน this.state.value ก็จะเหลือแค่ value อย่างเดียว

ถึงแม้ว่าเราจะสามารถนำ state และ lifecycle มาใส่ใน functional component ได้แล้ว แต่ว่า class component ก็จะยังไม่หายไปไหนนะครับ ใครที่ใช้ class component อยู่แล้วก็ไม่ต้องไปเปลี่ยนให้เป็น functional component ทั้งหมดก็ได้นะครับ