[TypeScript] 將TypeScript 導入到React專案的常見問題- 基本元件的Prop

施慶銓
7 min readApr 22, 2023

--

先前的文章
- [TypeScript] 將TypeScript 導入到React專案 — 起手式
- [TypeScript] 將TypeScript 導入到React專案的常見問題- createRoot參數錯誤
- [TypeScript] 將TypeScript 導入到React專案的常見問題- Props的型別與預設值

如何設定基本元件(Input, Button…)的Prop

與設定頁面的Prop不同的是, 頁面與頁面之間傳遞的Props通常只會設定頁面所需要的props. 例如

// SearchBar.jsx
function SearchBar() {
const [location] = useState({})

return (
<div>
<SearchTool :location="location" />
<LocationInfo :location="location" />
</div>
);
}


// LocationInfo.jsx
type Location = {
country: string,
city: string,
}

type Prop= {
location: Location
}

function LocationInfo({ location }: Prop) {
return (
<div>
{location.country} / {location.city}
</div>
)
}

例子中, 子元素因為需要顯示location 相關的資訊, 並且接收各種不同來自父元素的location 資訊, 所以子元素如SearchTool, LocationInfo內會特別建立一個location prop. 如果這時子元素還需要其他資訊, 才未在特別需要建立新的prop.

這時候, 子元素內的型別設定就相對單純, 只需要針對給定的prop 設定各個型別即可

type Location = {
country: string,
city: string,
}
type Prop= {
location: Location
}

但如果我們是建立一個新的元件如: Button, Input 等, 這些把原生的HTML 元素再做一層客製化設計, 讓這些元素更符合專案需求.

例如需要建立一個客製化的MyButton

function MyButton({
label,
type = "button",
className = "",
...props
}) {
return (
<button
type={type}
className={`${className} btn`}
{...props}
>
{label}
</button>
);
}

針對這個component, 因為設計上的關係, 所以特別設定了三個propstype, className以及label, 另外利用…props , 把所有在父元素設定的attribute 全部綁定到<button />上, 讓使用者在使用MyButton的時候也能像使用原生的<button />.

不過因為原生的<button /> 擁有的屬性以及事件很多, 而且也不一定每個屬性或是事件需要做特別的檢查或是設定, 只是單純的要綁定到<button /> 上, 這樣如果需要一個個特別設定每一個prop 會有點麻煩.

所幸React 針對這個問題, 也已經為這些原生的元素定義好型別

React 有提供一個套件, @types/react , 裡面提供了許多React 開發者在使用TypeScript 可能會用到的型別, 包含了原生HTML 元素的型別.

參考網址:

使用前

當我們為MyButton 加入TypeScript時, 可以像這樣

type Prop = {
label: string,
className?: string,
type?: "button" | "submit",
}

function MyButton({
label,
type = "button",
className = "",
...props
}: Prop) {
return (
<button
type={type}
className={`${className} btn`}
{...props}
>
{label}
</button>
);
}

不過有其他component 在使用MyButton, 使用了原生的button 事件, 如onClick, 在 MyButton 中可以透過spread operator的…props 直接綁定到<button /> 上, 但由於type 尚未定義, 所以會出現以下錯誤

錯誤提示訊息很清楚的說到 onClick沒有定義型別

解決的方式有幾種

  1. 在type 內定義onClick的型別
type Props = {
...,
onClick?: () => void,
}

2. 使用@typs/react提供的型別定義

type OwnProps = {
// 原本 type Prop內的型別
...,
}
type Props = OwnProps & React.ButtonHTMLAttributes<HTMLButtonElement>

ButtonHTMLAttributes 內容

關於ButtonHTMLAttributes,以下是ChatGPT的解釋

ButtonHTMLAttributes 是 React 提供的一個 TypeScript 泛型接口,用於定義 <button> 元素支持的所有 HTML 屬性。

這個接口包含了所有標準的 HTML 屬性,例如 classNameidstyle 等等,也包括了 <button> 元素獨有的屬性,例如 disabledtypevalue 等等。使用這個接口可以方便地聲明一個包含了所有可能的按鈕屬性的 props 類型。

在使用 <button> 元素時,可以透過繼承 ButtonHTMLAttributes<HTMLButtonElement> 的方式來聲明一個包含了所有可能的按鈕屬性的 props 類型,例如:

import { ButtonHTMLAttributes } from 'react';

interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
children: React.ReactNode;
}
const Button = (props: ButtonProps) => {
return (
<button {...props}>
{props.children}
</button>
);
};

在這個例子中,ButtonProps 類型繼承了 ButtonHTMLAttributes<HTMLButtonElement>,所以可以使用所有支援的 HTML 屬性,而不必自己聲明每個屬性。這樣可以讓代碼更加簡潔和易於維護。

以上兩個方法僅提供參考, 不管是直接在type 直接定義prop的型別, 還是使用@type/react 所提供的型別, 都可以參照個人習慣以及專案各自訂的風格以及元件設計的情境再決定使用, 並沒有絕對的使用情境.

--

--

施慶銓
施慶銓

No responses yet