🔰

Reactメモ

2023/07/22に公開4

前提

ReactもTypeScriptも初学者のため、基礎勉強のできるサイトや公式DOC、ZennやQiitaなどでインプットしてます。
アウトプットもしないとなあと思いこの度Zennに初投稿した次第です。
アドバイス・間違いの指摘大歓迎です。でも優しくお願いします🙇‍♀️

公式の例を元にTypeScriptに直してみる

公式のLEARNREACTよりReactインタラクティビティの追加をtypescriptで記述した版

型定義に悩んだのでメモ。

// 型定義
type ButtonProps = {
  onClick: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
  children: string;
};

// ボタンコンポーネント
const Button = ({ onClick, children }: ButtonProps) => {
  return <button onClick={onClick}>{children}</button>;
};

// 型定義
type ToolbarProps = {
  onButton1: () => void;
  onButton2: () => void;
};

// ツールバーコンポーネント
const Toolbar = ({ onButton1, onButton2 }: ToolbarProps) => {
  return (
    <>
      <Button onClick={onButton1}>ぼたん1</Button>
      <Button onClick={onButton2}>ぼたん2</Button>
    </>
  );
};

// Appコンポーネント
export default function App() {
  return (
    <Toolbar
      onButton1={() => alert("ぼたん1")}
      onButton2={() => alert("ぼたん2")}
    />
  );
}

型定義の名前の付け方もこれでいいのかわかりません…😂

AIにあってるか聞いた結果

あってるか不安でChatGPTに聞いてみました。
AIが正しいとは限らないので全部鵜呑みにして信じてしまうのはアレですが、参考にと思い。
長いので以下アコーディオン内に格納してます。

ChatGPTの返答

このReactコードはTypeScriptで記述されていますが、型定義は正しいです。コード全体の構造と型定義が一致していることを確認します。

  1. ButtonProps型定義:
type ButtonProps = {
  onClick: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
  children: string;
};

ButtonPropsは、onClickプロパティがReact.MouseEvent型のイベントを受け取り、何も返さない(void)コールバック関数であることを示しています。また、childrenプロパティは文字列である必要があります。この型定義はボタンコンポーネントのプロパティを正確に記述しています。

  1. Buttonコンポーネント:
const Button = ({ onClick, children }: ButtonProps) => {
  return <button onClick={onClick}>{children}</button>;
};

Buttonコンポーネントは、先ほど定義したButtonProps型の引数を受け取ります。コンポーネント内部でonClickchildrenを使用していますので、型定義とコンポーネントの実装が一致しています。

  1. ToolbarProps型定義:
type ToolbarProps = {
  onButton1: () => void;
  onButton2: () => void;
};

ToolbarPropsは、onButton1onButton2という2つのプロパティが、それぞれ何も受け取らずに何も返さない(void)コールバック関数であることを示しています。この型定義はツールバーコンポーネントのプロパティを正確に記述しています。

  1. Toolbarコンポーネント:
const Toolbar = ({ onButton1, onButton2 }: ToolbarProps) => {
  return (
    <>
      <Button onClick={onButton1}>ぼたん1</Button>
      <Button onClick={onButton2}>ぼたん2</Button>
    </>
  );
};

Toolbarコンポーネントは、先ほど定義したToolbarProps型の引数を受け取ります。このコンポーネント内でonButton1onButton2を使用していますので、型定義とコンポーネントの実装が一致しています。

全体的に見て、型定義とコンポーネントの実装が一致しており、正しく型付けされたReactコードとなっています。型エラーがない限り、問題なく動作するはずです。

参考

onClickの型定義で参考にさせていただきました。ありがとうございます。
https://zenn.dev/akkie1030/articles/9f2304544245b2

追記

アドバイスいただいたので早速。
しょっぱなの型定義部分について。

// 型定義
type ButtonProps = {
  onClick: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
  children: string;
};

これを、こう↓

type ButtonProps = {
  onClick: JSX.IntrinsicElements["button"]["onClick"],
  children: ReactNode,
};

もしくはこう↓

type ButtonProps = Pick<JSX.IntrinsicElements["button"], "onClick" | "children">

すると、さらに安全になるとのことでした。

JSX.IntrinsicElementsってなんぞや?ってなったので検索したりAIに聞いたりしました。

とりあえずファイル開いてみたら、IntrinsicElements interfacebuttonがあった。

["button"]でこのインターフェイスのbuttonを指定しているのかな。
で、この'button'にはonClickと'children'が存在しますよ〜〜ってことかな。

ちょっとまだ理解が追いつかないのでまた色々ググってみようと思います。

ちなみにAIの回答↓

ChatGPT

PickはTypeScriptのユーティリティ型の1つで、指定したオブジェクトから特定のプロパティを抽出するために使用されます。

type ButtonProps = Pick<JSX.IntrinsicElements["button"], "onClick" | "children">

は、JSX.IntrinsicElements["button"]という型(<button>要素に対応する型)から、onClickchildrenという2つのプロパティを抽出して、新しい型ButtonPropsを作成することを意味します。

Discussion

Honey32Honey32

問題はありませんが、

type ButtonProps = {
  onClick: JSX.IntrinsicElements["button"]["onClick"],
  children: ReactNode, // string よりも広く、全ての <hoge /> 形式(つまり JSX 式)を受け取れます
};

とか

type ButtonProps = Pick<JSX.IntrinsicElements["button"], "onClick" | "children">
// 

のような書き方をすると、よりミスの心配が減るのでオススメです。 button 要素の Props の型をそのまま引用できるので。

(Required<T> 型を使えば onClick, children を必須にすることもできます。)

みっちぇみっちぇ

早速アドバイス有難うございます!!
Pickは初めてみました。またあとで調べてみようと思います。
そういった定義の仕方があるのか!と勉強になります。

初歩の初歩を歩んでるとこなので、めちゃくちゃ助かります!ありがとうございます!

みっちぇみっちぇ

サバイバルTypeScriptのアドレスまでご親切にありがとうございます!ユーティリティ型、むずかしそ〜〜ってさらっと見て終わってました。もっかい丁寧に見て学習してみます!!!