這篇文章是我從事教學很常遇到的統整,我有系列專門解釋怎麼一步一步讓自己邁向 pro 的過程,有興趣了解更多的話可以參考這個連結,裡面有所有我所統整的React新手向教學。
相信剛進入 React 的新手在處理表單的時候都有以下的情況:
import { ChangeEvent, FC, useReducer, useState } from 'react'
const FormExample: FC = () => {
// 在此之前首先要了解 html input 的綁定機制
// 一般來說有兩種,受控(controlled) / 非受控(uncontrolled)
// 那這裡主要示範一般實務上應用最為廣泛的 controlled
// 上面的觀念我這裡就不再細講,有興趣深入理解的話可以 google 以上關鍵字
// 下面我使用一般初學的做法
// level 1
const [username, setUsername] = useState<string>('')
const [password, setPassword] = useState<string>('')
const lv1UsernameChange = (evt: ChangeEvent<HTMLInputElement>) => {
setUsername(evt.target.value)
}
const lv1PasswordChange = (evt: ChangeEvent<HTMLInputElement>) => {
setPassword(evt.target.value)
}
const levelSend = (e: ChangeEvent<HTMLFormElement>) => {
e.preventDefault();
console.log('level1', {username, password}); //level1
}
return (
<>
<div>
<h4> 表單應用的範例(登入系統)</h4>
</div>
<form onSubmit={levelSend}>
<fieldset>
<legend>Level 1</legend>
<div>
<label htmlFor="username">username</label>
<input
id="username"
name="username"
value={username}
onChange={lv1UsernameChange}
/>
</div>
<div>
<label htmlFor="password">password</label>
<input
id="password"
name="password"
type="password"
value={password}
onChange={lv1PasswordChange}
/>
</div>
</fieldset>
<button
type='submit'
style={{
margin: '1rem 0 0 0'
}}
>
送出
</button>
</form>
</>
)
}
export default FormExample
就是一個 Input 用一個 useState
去綁定,但其實你應該可以做到用一個 useState
去整合你所有頁面表單的資料結構。
簡單理解成給他一個 default 的物件資料結構,即可整合重複的段落,如下:
import { ChangeEvent, FC, useReducer, useState } from 'react'
const FormExample: FC = () => {
// level 2
// 將上述的欄位整合成一個 state 減少過度浪費暫存空間
const [level2, setLevel2] = useState({
lv2user: '',
lv2pwd: ''
})
// 這樣處理起來也可以簡化 change function 成一個共用 function
const level2Change = (e: ChangeEvent<HTMLInputElement>) => setLevel2({...level2, [e.target.name]: e.target.value })
const levelSend = (e: ChangeEvent<HTMLFormElement>) => {
e.preventDefault();
console.log('level2', level2) // level2
}
return (
<>
<div>
<h4>表單應用的範例(登入系統)</h4>
</div>
<form onSubmit={levelSend}>
<fieldset>
<legend>Level 2</legend>
<div>
<label htmlFor="lv2user">username</label>
<input
id="lv2user"
name="lv2user"
value={level2.lv2user}
onChange={level2Change}
/>
</div>
<div>
<label htmlFor="lv2pwd">password</label>
<input
id="lv2pwd"
name="lv2pwd"
type="password"
value={level2.lv2pwd}
onChange={level2Change}
/>
</div>
</fieldset>
<button
type='submit'
style={{
margin: '1rem 0 0 0'
}}
>
送出
</button>
</form>
</>
)
}
export default FormExample
或者,改用 useRef
的方式去綁定 input tag,透過 ref.current
的方式去取 input value 這樣一來可以不用再次定義 state change function,如下:
import { ChangeEvent, FC, useRef, useState } from 'react'
const FormExample: FC = () => {
const user_lv3Ref = useRef<HTMLInputElement>(null)
const pwd_lv3Ref = useRef<HTMLInputElement>(null)
const levelSend = (e: ChangeEvent<HTMLFormElement>) => {
e.preventDefault();
console.log('level3 ', {user_lv3: user_lv3Ref.current?.value, pwd_lv3: pwd_lv3Ref.current?.value});
}
return (
<>
<div>
<h4> 表單應用的範例(登入系統)</h4>
</div>
<form onSubmit={levelSend}>
<fieldset>
<legend>Level 3</legend>
<div>
<label htmlFor="user_lv3">username</label>
<input
id="user_lv3"
name="user_lv3"
ref={user_lv3Ref}
/>
</div>
<div>
<label htmlFor="pwd_lv3">password</label>
<input
id="pwd_lv3"
name="pwd_lv3"
type="password"
ref={pwd_lv3Ref}
/>
</div>
</fieldset>
<button
type='submit'
style={{
margin: '1rem 0 0 0'
}}
>
送出
</button>
</form>
</>
)
}
export default FormExample
利用 custom hook 的方式封裝 input
的 value
& onChange
事件,這個細節在我之前做 custom hook 系列文章中有分享到,細節時做的話我放在這個連結,我這篇的作法與 react-hook-form
所提供的useForm
hook 蠻相似的,但比較陽春一點,沒有整合 error 情境與 onBlur
事件的處理。
當然,這些做法就是一種思維邏輯的轉變,也有另外的可能直接透過第三方的 Library 來處理表單,例如:react-hook-form 這類的第三方套件,但作法我就不再贅述了,在我的 GitHub 教學連結裡面都有。
希望大家在使用套件的時候能理解為何而使用,以及它背後的知識點與運作邏輯,走出新手村的過程,就必須多多反思。
不要以為造輪子很蠢,真正蠢的是輪子為什麼而造都不知道,還傻傻的被第三方套件給綁住,萬一套件方不在持續更新的時候,漏洞產生你也無法修補,這些都是很有可能發生的情況。
走出新手村要慢慢開始學會思維成長,除了懂得選用對的套件工具之外,也必須懂得其實做的原理,這些實作的原理都是可以套用在其他開發情境的。