iT邦幫忙

2024 iThome 鐵人賽

DAY 26
0
自我挑戰組

React 開發者的 TypeScript 探索之旅系列 第 26

【 Day 26 】如何用 TypeScript 建立通用的 Input 元件

  • 分享至 

  • xImage
  •  

昨天,我們終於將 Todo List 的功能完成,今天開始,我們要來探索一些先前沒有機會用到的小技巧以及觀念。

在 Todo List 中,所有的 <input> 都是獨立的,對於 React 開發者來說,當有多個地方使用到 <input><button> ... 等時,我們會習慣將它直接作為一個元件,藉由傳入不同的 props,讓這個元件可以重複利用於不同的情境下。

今天我們以 <input> 來探索在 React 中使用 TypeScript 的小技巧。

首先,創建一個 Input 元件:

export default function Input() {
  return (
    <div>
      <label htmlFor=''></label>
      <input id='' />
    </div>
  )
}

label 的內容以及 id 我們會透過 props 傳入,因此在這邊需要為 props 定義型別:

type InputProps = {
  label: string
  id: string
}

export default function Input({ label, id }: InputProps) {
  return (
    <div>
      <label htmlFor={id}>{label}</label>
      <input id={id} />
    </div>
  )
}

回到 App.tsx 並匯入 Input 元件,我們可以透過傳遞不同的資訊,創建內容不同的 Input

import './App.css'
import Input from './components/Input'

function App() {
  return (
    <main>
      <Input id='account' label='Account' />
      <Input id='password' label='Password' />
    </main>
  )
}

export default App

目前我們僅僅是傳遞 id 以及 labelInput 元件,若我們需要傳入其他 <input> 可以使用的屬性呢?
常見的寫法是運用展開運算子:

export default function Input({ label, id, ...props }: InputProps) {
  return (
    <div>
      <label htmlFor={id}>{label}</label>
      <input id={id} {...props} />
    </div>
  )
}

預估達到的效果是可以對 Input 元件傳入其他屬性,像是 type

function App() {
  return (
    <main>
      <Input id='account' label='Account' type='text' />
      <Input id='password' label='Password' type='password' />
    </main>
  )
}

但這時候會收到錯誤訊息:
https://ithelp.ithome.com.tw/upload/images/20241006/20169025deBWIc1UAj.png

這是因為我們在 InputProps 中只定義了 idlabel 的型別,並沒有為 type 定義型別,在這邊我們會使用到 Day08 所介紹的合併型別,合併我們指定的型別 (id & label) 以及 <input> 所有內建方法、屬性的型別。
而 React 已經幫我們內建好了這個型別,也就是 ComponentPropsWithoutRef
合併 ComponentPropsWithoutRef 型別,並於泛型參數內傳入 input,告知 React 我們要使用的是 input 這個元素:

import { ComponentPropsWithoutRef } from 'react'

type InputProps = {
  label: string
  id: string
} & ComponentPropsWithoutRef<'input'>

export default function Input({ label, id, ...props }: InputProps) {
  return (
    <div>
      <label htmlFor={id}>{label}</label>
      <input id={id} {...props} />
    </div>
  )
}

ComponentPropsWithRefComponentPropsWithoutRef 的差別僅在於接收的 props 是否包含 ref,適合用於使用 forwardRef 時,而我們目前的範例不需要傳入 ref,所以選擇使用 ComponentPropsWithoutRef

打開瀏覽器,前往 http://localhost:5173/,你可以看見 password 欄位的 type 已經正確指定為 password 的形式。


上一篇
【 Day 25 】重構提示訊息
下一篇
【 Day 27 】Record, Pick, Omit
系列文
React 開發者的 TypeScript 探索之旅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言