ReactでChatGPTに使うようなフォームを作るための基礎知識

chatGPTのAPIを使って、AI同士で会話させるツールを作りました
（プロンプトエンジニアリングするときにuser側のメッセージ考えるのめんどくせえええって思って自動化してみました）

https://prompt-studio.vercel.app/

https://qiita.com/ruiiixiii/items/6a21eb83ab397ade4a16

さて、その時に使いやすいtextareaについての知見をいくつか得たのでそのまとめです。
chatGPTのAPIを使うときには長いinputがある分textareaを使うことも多いと思うので参考にしてみてください。

## 1. Ctrl+EnterまたはCmd+Enterキーでsubmitを実現する方法
テキストエリアに長い文章を入力するとき、Ctrl+EnterまたはCmd+Enterキーで送信する機能は便利です。この機能を実現するためには、onKeyDownイベントを使用してキーボード入力を監視する必要があります。本記事では、JavaScript/TypeScriptを使用して、この機能を実現する方法を紹介します。

### onKeyDownイベントについて
onKeyDownイベントは、ユーザーがキーボードのキーを押したときに発生します。このイベントを使用することで、特定のキーが押されたときに処理を実行することができます。例えば、Enterキーが押されたときにsubmit処理を行うといったことが可能です。

### Ctrl+Enter/Cmd+Enterキーでsubmitを実現する方法
以下のようなコードを使用して、Ctrl+EnterまたはCmd+Enterキーでsubmitを実現することができます。
```typescript
const handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
  // Cmd + Enter もしくは Ctrl + Enter
  if (event.key === "Enter" && (event.ctrlKey || event.metaKey)) {
    handleSubmit(onSubmit, onError)();
  }
};
```

このコードでは、onKeyDownイベントによってテキストエリアのキーボード入力を監視し、Ctrl+EnterまたはCmd+Enterキーが押された場合、handleSubmit関数を呼び出します。handleSubmit関数は、フォームのsubmit処理を行うための関数です。

### 実際のコード例
上記のコードをReactで実装する場合、以下のようになります。
react-hook-formを使っています
```tsx
import { useState } from "react";
import { useForm } from "react-hook-form";

type FormInput = {
  content: string;
};

const MyForm = () => {
  const { handleSubmit, register, reset } = useForm<FormInput>();
  const [inputValue, setInputValue] = useState("");

  const onSubmit = (data: FormInput) => {
    // フォームの送信処理
    console.log(data.content);
    reset();
  };

  const onError = (errors: any) => {
    // バリデーションエラー時の処理
    console.error(errors);
  };

  const handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
    // Cmd + Enter もしくは Ctrl + Enter
    if (event.key === "Enter" && (event.ctrlKey || event.metaKey)) {
      handleSubmit(onSubmit, onError)();
    }
  };

  const handleChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
    setInputValue(event.target.value);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit, onError)}>
      <textarea
        name="content"
        ref={register}
        onKeyDown={handleKeyDown}
        onChange={handleChange}
        value={inputValue}
      />
      <button type="submit">送信</button>
    </form>
  );
};
```

このコードでは、react-hook-formを使用してフォームの入力値を管理しています。テキストエリアの入力値は、useStateフックを使用して管理しています。onKeyDownイベントを使用して、Ctrl+EnterまたはCmd+Enterキーが押されたときに、handleSubmit関数を呼び出しています。

## 2. textareaの行数を自動調節する方法
多くのWebサイトやアプリケーションでは、長い文章の入力が必要な場合に、複数行のテキストエリアを提供しています。しかし、入力される文章の量に応じてテキストエリアの高さを調整する必要があるため、自動的に高さを調整する機能が必要となります。本記事では、reactとreact-hook-formを使用して、textareaの行数を自動調節する方法について解説します。

### textareaの高さを自動調節する方法
textareaの高さを自動的に調節するには、以下のようにonChangeイベントを利用して、textareaの高さを調整する必要があります。
```jsx
<textarea
  onChange={(e) => {
    e.target.style.height = "auto";
    e.target.style.height = e.target.scrollHeight + "px";
  }}
/>
```
この方法では、入力された文字列の高さを取得して、textareaの高さを自動的に調節しています。

### react-hook-formを利用したtextareaの高さの自動調節
react-hook-formを利用してtextareaの高さを自動調節する場合は、以下のようにonChangeイベントをregisterオプション内で利用する必要があります。

なぜならregisterにonChangeイベントが含まれており、register外で定義しても無効化されてしまうからです。そこだけ注意すれば簡単に実現できます。
```jsx
import { useForm } from "react-hook-form";

const MyForm = () => {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm();

  const onSubmit = (data) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <textarea
        {...register("content", {
          required: true,
          onChange: (e) => {
            e.target.style.height = "auto";
            e.target.style.height = e.target.scrollHeight + "px";
          },
        })}
      ></textarea>
      <button type="submit">送信</button>
    </form>
  );
};
```

## さいごに

https://prompt-studio.vercel.app/

最初に紹介したこのツールでは、これら含めてさまざまに使いやすいUIに改善をしています。
プロンプトエンジニアリング楽にしたい人、ただただAI同士で会話させて遊びたい人も含めて見に行ってもらえるとうれしいです！