[typescript] React Batter Wrapper Component (ComponentProps,ComponentPropsWithoutRef)
介紹
持續記錄在 udemy 的 ts 課程學到的東西,這次要介紹的是怎麼使用 ComponentPropsWithoutRef 來建立彈性化的 Wrapper Component。
問題點
現在有一個 Input component,程式碼如下,可以看到我們設定只能接收 id 和 label props。
type InputProps = {
  id: string;
  label: string;
};
const Input = ({ id, label, ...props }: InputProps) => {
  return (
    <p>
      <label htmlFor={id}>{label}</label>
      <input id={id} {...props} />
    </p>
  );
};
export default Input;
所以在使用 Input 的時候,我們只能傳入這兩個 props,如果傳入 type 或其他原生 HTML input 能夠使用的 attribute ,就會出現錯誤,因為 type 和 placeholder 並沒有定義在我們的 InputProps 裡面,但我們又不可能針對每一個 HTML element 的 attribute 去定義屬於它的 type。
import Input from "./components/Input";
function App() {
  return (
    <main>
      <Input
        id="name"
        label="Your name"
        type="text"
        placeholder="Your name ..."
      />
      <Input
        id="age"
        label="Your age"
        type="number"
        placeholder="Your age ..."
      />
    </main>
  );
}
export default App;
解決方法 (ComponentPropsWithoutRef)
還好 React 有提供 ComponentPropsWithoutRef 來解決這個問題,ComponentPropsWithoutRef 是一個泛型,使用的方法很簡單,只要傳入 HTML element 的 tag 名稱即可。
import { ComponentPropsWithoutRef } from "react";
type InputProps = {
  id: string;
  label: string;
} & ComponentPropsWithoutRef<"input">;
const Input = ({ id, label, ...props }: InputProps) => {
  return (
    <p>
      <label htmlFor={id}>{label}</label>
      <input id={id} {...props} />
    </p>
  );
};
export default Input;
現在回到 App,在 Input component 上按下 ctrl+i,就會看到原生 HTML input 能夠使用的 attribute 了。

補充
其實不只有 ComponentPropsWithoutRef 可以使用,還有 ComponentProps 和 ComponentPropsWithRef,用法也和字面上的意思一樣,如果 component 有傳入 ref 的話,就使用 ComponentPropsWithRef,差別就是讓維護的人看到就知道該 component 是需要接收 ref 的,而如果單純使用 ComponentProps 的話也可以,只是就沒辦法明確的知道 component 需不需要傳入 ref。
ComponentPropsWithoutRef 和 ComponentPropsWithRef 的缺點就是命名過長。
參考資料
ComponentProps: React's Most Useful Type Helper
udemy 課程 React & TypeScript - The Practical Guide Academind by Maximilian Schwarzmüller, Maximilian Schwarzmüller