React特有の型定義
ReactをTypeScriptで書いたときの型定義が個人的にややこしかったのでまとめてみます。
すべて挙げるともっとたくさんあるのでしょうが、家計簿アプリ(Udemy教材)を以前作成したときに出てきたものを中心にピックアップしました。
<目次>
- React.ReactNode
- React.FC
- React.ChangeEvent
- React.MouseEvent
- React.Dispatch
- React.SetStateAction
- React.ComponentType
1. React.ReactNode
コンポーネントの子要素として渡せるすべての型に対して記述するユニオン型。
ユニオン型・・・2つ以上の型をパイプ記号(|)で繋げて書く。
type ReactNode = string | number | bigint | boolean | ReactElement<any, string |
JSXElementConstructor<any>> | Iterable<ReactNode> | ReactPortal | Promise<...> |
null | undefined
ReactElement型も包括する。
ReactElement型・・・JSX要素のみを指し、文字列や数値のようなプリミティブ値は含まない。
2. React.FC
FCはFunctionComponentの略。
関数コンポーネントに対して定義する。
type Props = { message: string };
const MyComponent: React.FC<Props> = ({ message }) => <p>{message}</p>;
上記のように使用して、propsの型定義ができる。
props・・・親コンポーネントから子コンポーネントに渡すデータ のこと。
コンポーネントの戻り値の型(JSX.Element)を省略できる。
最近のTypeScriptではReact.FCを使わずに書くのが推奨される場合もある。
type Props = { title: string };
const MyComponent = ({ title }: Props): JSX.Element => {
return <h1>{title}</h1>;
};
3. React.ChangeEvent
フォーム要素(input、textarea、select など)の onChange イベントハンドラーで使われる。
const TextInput = () => {
const [text, setText] = useState("");
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setText(event.target.value);
};
return (
<input type="text" value={text} onChange={handleChange} />
);
};
4. React.MouseEvent
マウス操作に関連するイベント(click、mouseover、mousedown など)を扱うときに使われる。
const ButtonComponent = () => {
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
console.log("Button clicked!", event);
};
return <button onClick={handleClick}>Click Me</button>;
};
『5. React.Dispatch』 & 『6. React.SetStateAction』
React.Dispatch・・・状態更新関数(setState)の型を表す。
React.SetStateAction・・・状態更新のために渡す値の型を表す。通常、setState 関数に渡す値の型として使う。
下記の場合だと、prevCount が React.SetStateAction<number> 型
const [count, setCount] = useState<number>(0);
// setCount: React.Dispatch<React.SetStateAction<number>>
const increment = () => {
setCount(prevCount => prevCount + 1); // prevCount は React.SetStateAction<number> 型
};
7. React.ComponentType
コンポーネントの型を定義するために使う。特に、コンポーネントを動的に扱うときや、他のコンポーネントに渡すときに便利。
コンポーネント・・・UIを構成する部品のこと。
React.ComponentType の使いどころ
-
コンポーネントを props として渡す場合
-
コンポーネントの型が動的に決まる場合
-
高階コンポーネントを作成するとき
-
汎用的なコンポーネントを受け取るとき
上記のうち、2以外の例を挙げます。
(例)1. コンポーネントを props として渡す場合
コンポーネントが受け取る props の型 と、返す JSX 要素の型 を指定するために使われる。
// 任意の Props を持つコンポーネントの型
type MyComponentProps = {
message: string;
};
const MyComponent: React.FC<MyComponentProps> = ({ message }) => {
return <p>{message}</p>;
};
// コンポーネントを props として受け取るコンポーネント
type WrapperProps = {
Component: React.ComponentType<MyComponentProps>;
};
const Wrapper: React.FC<WrapperProps> = ({ Component }) => {
return <Component message="Hello, World!" />;
};
const App: React.FC = () => {
return <Wrapper Component={MyComponent} />;
};
chatGPTに挙げてもらった例で少し分かりにくいですが、「Component」 コンポーネントが「MyConponent」コンポーネントをpropsとして受け取っています。
(例)3. 高階コンポーネントを作成するとき
高階コンポーネントは、他のコンポーネントをラップして拡張するパターン。
このときに React.ComponentType を使って、引数や返り値としてコンポーネントを扱う。
// 受け取るコンポーネントの型
type WithLoadingProps = {
isLoading: boolean;
};
// 高階コンポーネントの型
const withLoading = <P extends object>(
Component: React.ComponentType<P>
) => {
return (props: P & WithLoadingProps) => {
if (props.isLoading) {
return <div>Loading...</div>;
}
return <Component {...props} />;
};
};
// 基本のコンポーネント
const MyComponent: React.FC<WithLoadingProps> = ({ isLoading }) => {
return <div>{isLoading ? "Loading..." : "Data Loaded!"}</div>;
};
// 高階コンポーネントを使用
const MyComponentWithLoading = withLoading(MyComponent);
const App: React.FC = () => {
return <MyComponentWithLoading isLoading={false} />;
};
(例)4. 汎用的なコンポーネントを受け取るとき
実際に家計簿アプリの中で使っていたのはこれかなと。
interface menuItem {
text: string | React.ReactNode,
path: string,
icon: React.ComponentType,
}
const Sidebar = ({drawerWidth, mobileOpen, handleDrawerTransitionEnd, handleDrawerClose}: SidebarProps) => {
const pathname = usePathname();
const MenuItems: menuItem[] = [
{text: "Home", path: "/home", icon: HomeIcon},
{text: <Typography>月間<br />レポート</Typography>, path: "/report", icon: QueryStatsIcon},
]
iconに対してComponentTyepを適用しています。
- アイコンは単なる画像ではなく、インタラクティブな要素やスタイリングを含む可能性がある
- 関数コンポーネントとクラスコンポーネントの両方の型を受け入れることができる→様々なアイコンライブラリ(Material-UI等)のコンポーネントを使用できる
- アイコンコンポーネントは、サイズ、色、その他のスタイルプロパティを受け取る必要がある場合がある
<React.ComponentType と React.FC の違い>
React.FC (Functional Component) は、コンポーネントに渡される children プロパティ を自動的に扱う。
React.ComponentType は、コンポーネントの型を props の型を含めて 定義するために使う。children を考慮せず、より汎用的な型を提供する。
参考資料:
さいごに
今回思ったのは公式ドキュメントみたいなものがはっきりしなくて、中々調べにくいですね。
最初にコードを書こうとしたときはReactNodeとかFCとか、ぱっと見て何のことか非常に分かりにくかったです。
今後も使う種類が増えたときにまた調べ直してみたいと思います。
Discussion