formでよく使うElementの型定義覚え書き

「`button` や `input` の型定義ってどうだっけ？」
作成するたびにググって確認しているので、いい加減覚えないと。
ということでアウトプットを兼ねて書き記しておきます。


```
//tsのバージョン
typeScript Version 4.6.4
```

せっかくなので、Reactでコンポーネントを作りながらやっていまきす。
今回のディレクトリ構成はひとまずこんな感じにしました。
後でしっかり読みたい記事はこちら : [私のフロントエンドディレクトリ構成・テスト観点 2022](https://zenn.dev/takepepe/articles/nextjs-testing-strategy-2022)
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206790/35c8d6c2-8344-6276-e0aa-d5c80eca0967.png" width="300">

### App.tsx

App.tsxは、コンポーネントを並べるだけです。

```jsx
import { useRef } from 'react';
import { Button } from './components/common/form/element/Button'
import { Input } from './components/common/form/element/Input'
import { CheckBox } from './components/common/form/element/CheckBox';
import { Select } from './components/common/form/element/Select';
import { TextArea } from './components/common/form/element/TextArea';

export const App = () => {

  const handler = () => {
    console.log("hello")
  }

  const fruits = [
    { id: 1, name: 'りんご' },
    { id: 2, name: 'バナナ' },
    { id: 3, name: 'メロン' },
  ]

  const ref = useRef<HTMLButtonElement>(null!)
  const catchRef = () => {
    console.log(ref.current)
  }

  return (
    
    <div className="App">
      <button onClick={catchRef}>Buttonのref.current</button>
      <Button
        onClick={handler}
        type='reset'
        name='testButton'
        className='resetButton'
        ref={ref}
      >
        <span>次へ</span>
      </Button>

      <br />

      <Input
        onFocus={handler}
        name='testInput'
        className='inputText'
      />

      <br />
      <CheckBox
        name="fruits"
        defaultChecked={false}
        className='checkBox'
      />
      <br />

      <Select
        name="fruits"
        className='checkBox'
        option={fruits}
      />

      <br />
      <TextArea
        onFocus={handler}
        name='testInput'
        className='textArea'
        placeholder='ご要望'
      />

    </div>
  );
}

```

## 使う

`ComponentProps`を用いて型を指定する時、refを含んでいるかを明確にするため 
`ComponentPropsWithoutRef` , `ComponentPropsWithRef` を使用します。
[結局 React のコンポーネントにはどうやって型をつければ良いのさ - 蛇足部分](https://kk-web.link/blog/20201023)


### Button component

`HTMLxxxElement` は、`HTMLElement`の詳細。
Buttonの場合は、 `「HTMLElementの中のHTMLButtonElementだよ」` とジェネリクスで書く必要がある。
[HTMLElementはこちら](https://developer.mozilla.org/ja/docs/Web/API/HTMLElement)

```jsx
import { ComponentPropsWithoutRef, forwardRef } from "react";

export const Button = forwardRef<
  HTMLButtonElement,
  ComponentPropsWithoutRef<"button">
>(function ButtonElement({ className, ...props }, ref) {
  return (
    // スプレット構文でまるっと渡す
    <button {...props} ref={ref} />
  );
});


```


### Input component

Buttonコンポーネントとほぼ同じように書くことができる。
`HTMLInputElement` と宣言するように！ 

```jsx
import { ComponentPropsWithoutRef, forwardRef } from "react";

export const Input = forwardRef<
  HTMLInputElement,
  ComponentPropsWithoutRef<"input">
>(function InputElement({ className, ...props }, ref) {
  return (
    <input
      type="text"
      {...props}
      ref={ref}
    />
  );
});
```

### TextArea component

Buttonコンポーネントとほぼ同じように書くことができる。
`HTMLTextAreaElement` と宣言するように！ 
*今回は使っていませんが、自動的に高さが可変する [react-textarea-autosize](https://www.npmjs.com/package/react-textarea-autosize) はおすすめです。

```jsx
import { ComponentPropsWithoutRef, forwardRef } from "react";

export const Textarea = forwardRef<
  HTMLTextAreaElement,
  ComponentPropsWithoutRef<"textarea">
>(function TextareaElement({ className, ...props }, ref) {
  return (
    <textarea {...props} ref={ref}/>
  );
});
```

### CheckBox component

もうちょっと改良の余地があると思っています。
引き続き調べる。

```jsx
type Props = {
  name: string
  defaultChecked: boolean
  className: string
}

export const CheckBox = ({ ...props }: Props) => {
  return <input type='checkbox' {...props} />
}
```

### Select component

下記は簡略的な書き方。
こちらは合わせて `HTMLSelectElement` についてもしっかり覚えておく必要がある。
[React コンポーネントで入力フォームを作成する (1) 自力編](https://maku.blog/p/tdpybmw/)
 
```jsx
import { FC } from 'react';

type SelectType = JSX.IntrinsicElements["select"]

// こちらでもOK
// type Option = {
//   option: {
//     id: number
//     name: string
//   }[]
// }
// type Props = SelectType & Option

type Option = {
  id: number
  name: string
}
type OptionProps = {
  option: Option[]
}

type Props = SelectType & OptionProps

export const Select: FC<Props> = ({ ...props }) => {
  return (
    <select {...props}>
      {props.option.map((data) =>
        <option key={data.id}>
          {data.name}
        </option>
      )}
    </select >
  )
}

```

---

formで使用するElementは汎用的にしておきたいですよね。
覚えはじめは苦戦しましたが、1つ１つ理解していけばするすると入ってきました。
まだふわっとしている部分もありますが、これから固めていきますよ！！！！！