react-hook-from textareaの値を改行区切りで取得する

管理画面系などたまーに下記の画像のように改行区切りで値を取得したい時があります。

console

そんなときは、サンプルコードのようにすればyupと組み合わせたときでも問題なく値を取得することが可能です。

import { NextPage } from 'next';
import { Controller, useForm } from 'react-hook-form';

import React, { ChangeEvent } from 'react';
import * as yup from 'yup';
import { yupResolver } from '@hookform/resolvers/yup';

const schema = yup.object({
  word: yup.array().required().of(yup.string().required()),
});

type InputType = yup.InferType<typeof schema>;

/**
 * TextAreaで改行区切りの値を扱う
 */
const TextArea: NextPage = () => {
  const { control, handleSubmit } = useForm<InputType>({
    resolver: yupResolver(schema),
  });
  const onSubmit = (data: InputType): void => console.log(data.word);

  const parseArray = (e: ChangeEvent<HTMLTextAreaElement>): string[] => {
    return e.target.value.split('\n');
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Controller
        control={control}
        name={'word'}
        defaultValue={[]}
        render={({ field: { onChange, value } }): JSX.Element => (
          <textarea
            placeholder={'word1\nword2\nword3'}
            rows={5}
            onChange={(e): void => onChange(parseArray(e))}
            value={value?.join('\n')}
          />
        )}
      />
      <input type="submit" />
    </form>
  );
};

export default TextArea;

ポイント

  • おなじみのregisterではなく、Controllerとcontrolを使用する
  • Controllerのnameでfield名を指定する
  • renderでcomponentを返却する際に、onChangeで配列に変換し、valueで改行つきの文字列に変換する
  • defaultValueで初期値を設定しないとエラーになる

まとめ

ユーザインターフェースがわかりにくいので、普段はあまり使われず、例が少なかったのでまとめてみました。

react-hook-form #2 validationを行いたい(yup)

大変便利なreact-hook-form。 便利なreact-hook-formにはdefaultで使えるvalidation機能があります。

<input {...register('firstName', { required: true, max: { value: 10, message: 'error' } })} />

このような形でregisterで登録するときに、optionsを指定すれば、ある程度のvalidationを実装することは可能です。 ただ、複数の入力項目になった場合、componentの内部で設定を持つと見にくいですし、設定の仕方も少し直感的ではないです。 そこで、react-hook-formではschema baseのvalidationを使用して、改善することができます。 今回はyupというmoduleを入れて、実装していきます。

導入

下記のmoduleを導入してください。

npm install @hookform/resolvers yup

サンプル

import { NextPage } from 'next';
import { useForm } from 'react-hook-form';

import React from 'react';
import * as yup from 'yup';
import { yupResolver } from '@hookform/resolvers/yup';

// validaton schemaの定義
const schema = yup.object({
  firstName: yup.string().required(),
  age: yup.number().positive().integer().required(),
});

// useFormで使用する型を定義
type InputType = yup.InferType<typeof schema>;

const Validation: NextPage = () => {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm<InputType>({
    resolver: yupResolver(schema),
  });
  const onSubmit = (data: InputType): void => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('firstName')} />
      <p>{errors.firstName?.message}</p>

      <input {...register('age')} />
      <p>{errors.age?.message}</p>

      <input type="submit" />
    </form>
  );
};

export default Validation;

すごく簡単にvalidatonを拡張できますね! 事前にschemaを定義して、useFormでresolverを指定するだけで、validatonを行うことができます。 yupのほかにもZod,Superstruct,Joiといったライブラリに対応をしています。

お好きなライブラリを入れてみるといいかもですね!

react-hook-form #1 stateまみれのcomponentを改善したい

目的

reactのform componentを書くときに入力値をstateで管理して、stateまみれのcomponentを作ってしまって可読性を下げてしまうことってありませんか? 例えば、こんな感じ。

import {NextPage} from 'next';
import {useState} from 'react';

const StartPage: NextPage = () => {
  const [example, setExample] = useState('');
  const [exampleRequired, setExampleRequired] = useState('');

  return (
    <form>
      <input name="example" value={example} onChange={(e): void => setExample(e.target.value)} />
      <input
        name="exampleRequired"
        value={exampleRequired}
        onChange={(e): void => setExampleRequired(e.target.value)}
      />
      <input type="submit" />
    </form>
  );
};

export default StartPage;

項目数が少ないと上記のようなコードでも大丈夫ですが、項目数が増えて、さらに、項目ごとのエラーをstateで管理するってなったら、state地獄になってしまいますよね。
stateを乱立させずに、入力値を管理できるようにしたい。
そんな時に使用するのが、react-hook-formです。

導入手順

導入の仕方はすごく簡単で、以下のライブラリをinstallするだけです。ほかに依存しているライブラリなどはありません。

npm install react-hook-form

改善

react-hook-formを導入すれば、目的で書いたよう例題はこのように改善されます。

import {NextPage} from 'next';
import {useForm} from 'react-hook-form';

const StartPage: NextPage = () => {
  const {
    register,
    handleSubmit,
    watch,
    formState: { errors },
  } = useForm();

  const onSubmit = (data: any): void => console.log(data);

  // watch input value by passing the name of it
  console.log(watch('example'));

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      {/* register your input into the hook by invoking the "register" function */}
      <input defaultValue="test" {...register('example')} />

      {/* include validation with required or other standard HTML validation rules */}
      <input {...register('exampleRequired', { required: true })} />
      {/* errors will return when field validation fails  */}
      {errors.exampleRequired && <span>This field is required</span>}

      <input type="submit" />
    </form>
  );
};

引用元#exaple

react-hook-form.com

ポイント

  • exampleやexapleRequiredなどの個別のstateを用意せずに済む。
  • useFormのhooksでformのhande処理、項目のエラーなど様々なものが用意されている。

簡単な解説

まず、初めに、useFormでreact-hook-formを使用する準備を行います。

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

register

registerはinputタグに使用します。使用する際に名前(exaple)を付与して、以後はその名前でvalue値を取得できるようになります。

<input defaultValue="test" {...register('example')} />

hadlesumit

formのonSubmitの処理をhadleするのに使用します。

<form onSubmit={handleSubmit(onSubmit)}>

watch

inputで付与した名前に対して、値を監視することができます。

console.log(watch('example'));

formState

エラーなどのformに関する値を様々に管理しています。

  {errors.exampleRequired && <span>This field is required</span>}

実際に画面を動かしてみた

値を入力して送信ボタンを押したところ、consoleにexampleとexampleRequired の値が確認できる。

TypeScriptでFormの型を指定する場合

typeを作成し、useFormのジェネリクスに型を指定すれば、Formのデータ型を指定することができ、registerでtype以外の名前を使用することができなくなる。

type FormType = {
  example: string;
  exampleRequired: string;
};

const StartPage: NextPage = () => {
  const {
    register,
    handleSubmit,
    watch,
    formState: { errors },
  } = useForm<FormType>();

  const onSubmit = (data: any): void => console.log(data);

  // watch input value by passing the name of it
  console.log(watch('example'));

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      {/* register your input into the hook by invoking the "register" function */}
      <input defaultValue="test" {...register('example')} />

      {/* include validation with required or other standard HTML validation rules */}
      <input {...register('exampleRequired', { required: true })} />
      {/* errors will return when field validation fails  */}
      {errors.exampleRequired && <span>This field is required</span>}

      <input type="submit" />
    </form>
  );
};

export default StartPage;

まとめ

state管理に苦しまなずに済む。

おススメのライトノベルです。是非、ぽちってください。