Reactでよく使う型定義
はじめに
随時更新していきます。
onSubmitのtarget
form要素のonSubmitイベントのtargetは、name属性が型定義されていないので、型定義を追加する必要があります。
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault(); // デフォルトの挙動をキャンセル
// name属性を指定しているinput要素の値を取得
const target = e.target as typeof e.target & {
email: { value: string };
password: { value: string };
};
const email = target.email.value; // type: string
const password = target.password.value; // type: string
};
return (
<form onSubmit={handleSubmit}>
<input type="email" name="email" />
<input type="password" name="password" />
<button type="submit">ログイン</button>
</form>
)
Buttonのtype
aタグの子要素としてbutton要素を使うのは、HTMLの仕様上、許可されていません。
そのため、コンポーネント化した場合はhref属性をpropsとして受け取りつつ、hrefの有無でaタグかbutton要素を出し分ける必要があります。
アロー関数ではなく、function宣言で書くと、関数のオーバーロードを使って、型定義を簡潔に書くことができます。
:::details修正前
import {
AnchorHTMLAttributes,
ButtonHTMLAttributes,
DetailedHTMLProps,
} from "react";
function Button(
props: DetailedHTMLProps<
ButtonHTMLAttributes<HTMLButtonElement>,
HTMLButtonElement
>,
): JSX.Element;
function Button(
props: DetailedHTMLProps<
AnchorHTMLAttributes<HTMLAnchorElement>,
HTMLAnchorElement
>,
): JSX.Element;
function Button(
props:
| DetailedHTMLProps<
ButtonHTMLAttributes<HTMLButtonElement>,
HTMLButtonElement
>
| DetailedHTMLProps<
AnchorHTMLAttributes<HTMLAnchorElement>,
HTMLAnchorElement
>,
): JSX.Element {
if ("href" in props) {
return <a href={props.href} {...props} />;
} else {
const { ...buttonProps } = props as DetailedHTMLProps<
ButtonHTMLAttributes<HTMLButtonElement>,
HTMLButtonElement
>;
return <button type={buttonProps.type || "button"} {...buttonProps} />;
}
}
export default Button;
:::
もっと簡潔に書けるとご指摘をいただきましたので、修正しました。
コメントありがとうございます!
ComponentPropsWithoutRefを使った場合
import type { ComponentPropsWithoutRef } from "react";
function Button(props: ComponentPropsWithoutRef<"a">): JSX.Element;
function Button(props: ComponentPropsWithoutRef<"button">): JSX.Element;
function Button(
props: ComponentPropsWithoutRef<"button"> | ComponentPropsWithoutRef<"a">,
): JSX.Element {
if ("href" in props) {
return <a href={props.href} {...props} />;
} else {
const { ...buttonProps } = props as ComponentPropsWithoutRef<"button">;
return <button type={buttonProps.type || "button"} {...buttonProps} />;
}
}
export default Button;
JSX.IntrinsicElementsを使った場合
function Button(props: JSX.IntrinsicElements["a"]): JSX.Element;
function Button(props: JSX.IntrinsicElements["button"]): JSX.Element;
function Button(
props: JSX.IntrinsicElements["button"] | JSX.IntrinsicElements["a"],
): JSX.Element {
if ("href" in props) {
return <a href={props.href} {...props} />;
} else {
const { ...buttonProps } = props as JSX.IntrinsicElements["button"];
return <button type={buttonProps.type || "button"} {...buttonProps} />;
}
}
export default Button;
useStateの型定義
useStateで初期値を []
や {}
で初期化すると、型推論が効かないので、明示的に型定義する必要があります。
const [count, setCount] = useState(0); // type: number
const [text, setText] = useState(""); // type: string
const [flag, setFlag] = useState(false); // type: boolean
const [counts, setCounts] = useState<number[]>([]); // []で初期化すると、型推論が効かないので、明示的に型定義する
fetchの型定義
fetchのレスポンスをjson()メソッドを使って、json形式に変換した場合、型推論が効かないので、明示的に型定義する必要があります。
const [data, setData] = useState<DataType[]>([]); // []で初期化すると、型推論が効かないので、明示的に型定義する
useEffect(() => {
fetch("https://example.com/api")
.then((res) => res.json())
.then((data) => setData(data));
}, []);
useSWRを使った場合
useSWRの場合は、以下のように型定義することで、型推論が効きます。
const { data, error } = useSWR<DataType[]>("https://example.com/api");
Next.jsのServer Componentsを使った場合
Next.jsのServer Componentsを使った場合は、以下のように型定義することで、型推論が効きます。
export default async function Page() {
const data: DataType[] = await fetch("https://example.com/api").then((res) => res.json());
return <Component data={data} />;
}
useRefの型定義
useRefフックは、Reactのレンダリングサイクルの間で値を保持するために使用されます。useRefは、引数として初期値を受け取り、その値を.currentプロパティに保持するオブジェクトを返します。
useRefの型定義は、ジェネリクスを使用して行います。
const ref = useRef<HTMLDivElement>(null);
useRefはHTMLDivElementのインスタンスを参照します。
ref.currentはHTMLDivElementかnullになります。
この先工事中🚧
(最終更新日: 2023-11-23)
Discussion
失礼します。
フォームの各要素の値取得
onSubmit
でのフォームの各要素の値の取得は、FormData
というクラスを使うことができます。ただ、FormData
では各プロパティがstring | File
型なので、 zod-form-data のようなスキーマライブラリが使えそうです。zod を普通に使うだけでも良さそう
DetailedHTMLProps よりも短く
を使うと遠回りせず、かなり短く書けます。
コメントありがとうございます。
zodは外部ライブラリなのでこの記事では取り扱わない方針です。
ComponentPropsWithoutRefは初めて知りました。ありがとうございます。
修正しておきます。