การจัดการ State ของ Form ใน Class Component ของ React นั้นเป็นสิ่งที่ยุ่งยากอย่างมาก ยิ่งเมื่อเทียบกับคู่แข่ง เช่น Angular และ Vue เรียกได้ว่าเป็นจุดอ่อนของ React จุดนึงเลยทีเดียว ในบทความนี้จะเป็นการใช้ของเล่นใหม่ที่เรียกว่า React Hooks มาช่วยในการจัดการ State ของ Form ซึ่งเมื่อเทียบกับวิธีการเดิมแล้วเรียกได้ว่าสะดวกและง่ายกว่าเดิมมากครับ
ในตอนท้ายของบทความเราจะลอง Implement Custom Hooks อย่างง่ายเพื่อเป็นการ Reuse Logic ในการจัดการ State ของ Form ด้วยเช่นกันครับ
Path
+-src
| |-FormApplication.jsx
| |-custom-hooks.js
เรามาเริ่มต้นด้วยการเขียน Functional Component ตามตัวอย่างโค้ดด้านล่างครับ
ไฟล์ src/FormApplication.jsx
import React from 'react'
function FormApplication() {
return <div>
<input type="text" />
<input type="text" />
</div>
}
เมื่อไหร่ก็ตามที่เราต้องการใช้งาน State ภายใน Funtional Component ของ React ก็ต้องนี่เลยครับ useState()
ใช้งานง่ายตรงไปตรงมา
ไฟล์ src/FormApplication.jsx
import React, { useState } from 'react'
function FormApplication() {
const [ firstName, setFirstName ] = useState('Steve')
const [ lastName, setLastName ] = useState('Jobs')
return <div>
<input type="text" value={ firstName } />
<input type="text" value={ lastName } />
</div>
}
useState()
คืนค่าในแบบ Array เนื่องจากลำดับของ Array เป็นสิ่งสำคัญโดยตำแหน่งที่ 0 จะเป็นตัว State ในขณะที่ตำแหน่งที่ 1 จะเป็น Method ที่ทำหน้าที่ในการ Mutate State นั้นๆ
onChange
Eventเมื่อเกิดการเปลี่ยนแปลงค่าใน <input />
จะเกิด onChange
Event ขึ้น สิ่งที่เราต้องการคือนำ Method ที่จะทำการ Mutate State ของเรามา Binding กับ onChange
props
ของ <input />
ซึ่งหากมีการ Invoke Method ดังกล่าวก็จะเป็นการ Trigger ให้เกิดการ Re-render ใหม่ครับ
โดยเราสามารถ Binding Method จาก Hooks โดยตรงหรือกำหนด Nested Function เพื่อเรียกใช้งาน Method จาก Hooks อีกทอดก็ได้เช่นกัน
ไฟล์ src/FormApplication.jsx
function FormApplication() {
const [ firstName, setFirstName ] = useState('Steve')
const [ lastName, setLastName ] = useState('Jobs')
function handleChangeLastName(e) {
setLastName(e.target.value)
}
return <div>
<input type="text" value={ firstName } onChange={ e => setFirstName(e.target.value) } />
<input type="text" value={ lastName } onChange={ handleChangeLastName } />
</div>
}
เพื่อเป็นการ Reuse ส่วนของการจัดการ onChange
Event เราจึงใช้วิธีการกำหนด Custom Hooks อย่างง่าย ขึ้นมาสักตัว โดย Custom Hooks ก็คือ JavaScript Function นั่นเอง
เราจะตั้งชื่อ Custom Hooks ตัวนี้ว่า useFormInput
ซึ่งจะรับ Argument เพื่อใช้เป็นค่าเริมต้นและ Custom Hooks เองก็สามารถจะเรียกใช้ Hooks ตัวอื่นๆ ได้เช่นกัน ในส่วนท้ายเจ้า useFormInput
ก็จะต้องคืนค่า State และ Mutate Method ในแบบ Plain JavaScript Object ครับ
ไฟล์ custom-hooks.js
export default function useFormInput(initValue) {
const [ value, setValue ] = useState(initValue)
function handleChange(e) {
setValue(e.target.value)
}
return {
onChange: handleChange,
value
}
}
เราสามารถคืนค่าจาก Custom Hooks ในแบบ Array เหมือนกับกรณีของ
useState()
ได้เช่นกัน แต่เพื่อความสะดวกในการนำไปใช้งานในลำดับต่อไปเราจึงเลือกที่จะคืนค่าในแบบ Plain JavaScript Object ครับ
useFormInput()
Hooksมาถึงขั้นตอนสุดท้ายแล้วนะครับ นั่นก็คือการนำ useFormInput()
มาใช้นั่นเอง
ไฟล์ src/FormApplication.jsx
import useFormInput from './custom-hooks'
function FormApplication() {
const { value, onChange } = useFormInput('Steve')
const lastName = useFormInput('Jobs')
return <div>
<input type="text" onChange={ onChange } value={ value } />
<input type="text" { ...lastName } />
</div>
}
ในกรณีของ
useFormInput()
นั้นการคืนค่าในแบบ Plain JavaScript Object จะมีความสะดวกในการนำไป Binding มากกว่าเนื่องจากเราสามารถใช้ Spread Syntax ซึ่งทำให้โค้ดที่เขียนค่อนข้างจะกระชับกว่าพอสมควร