iT邦幫忙

2023 iThome 鐵人賽

DAY 15
0
Modern Web

React 走出新手村 系列 第 15

React 走出新手村 — 表單進化論

  • 分享至 

  • xImage
  •  

表單處理

這篇文章是我從事教學很常遇到的統整,我有系列專門解釋怎麼一步一步讓自己邁向 pro 的過程,有興趣了解更多的話可以參考這個連結,裡面有所有我所統整的React新手向教學。

https://ithelp.ithome.com.tw/upload/images/20230911/20129020LuVqZT7lZq.png

Level 1

相信剛進入 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 去整合你所有頁面表單的資料結構。

Level 2

簡單理解成給他一個 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

Level 3

或者,改用 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

Level 4

利用 custom hook 的方式封裝 inputvalueonChange 事件,這個細節在我之前做 custom hook 系列文章中有分享到,細節時做的話我放在這個連結,我這篇的作法與 react-hook-form 所提供的useForm hook 蠻相似的,但比較陽春一點,沒有整合 error 情境與 onBlur 事件的處理。

第三方套件

當然,這些做法就是一種思維邏輯的轉變,也有另外的可能直接透過第三方的 Library 來處理表單,例如:react-hook-form 這類的第三方套件,但作法我就不再贅述了,在我的 GitHub 教學連結裡面都有。

總結

希望大家在使用套件的時候能理解為何而使用,以及它背後的知識點與運作邏輯,走出新手村的過程,就必須多多反思。

不要以為造輪子很蠢,真正蠢的是輪子為什麼而造都不知道,還傻傻的被第三方套件給綁住,萬一套件方不在持續更新的時候,漏洞產生你也無法修補,這些都是很有可能發生的情況。

走出新手村要慢慢開始學會思維成長,除了懂得選用對的套件工具之外,也必須懂得其實做的原理,這些實作的原理都是可以套用在其他開發情境的。

給全新手的大禮包

React基本Hook教學


上一篇
React 走出新手村-高階組件 (H.O.C.)
下一篇
React 走出新手村 — 樣式的選擇
系列文
React 走出新手村 31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言