LIGのメルマガ、はじめました!
LIGのメルマガ、はじめました!
2020.03.19
#3
Next.jsを勉強してみる

Next.jsを勉強してみる その③ 〜入力チェック(バリデーション)編〜

やなさん

まいどです。バックエンドエンジニアのやなさんです。

さて、前回、前々回でNext.jsの環境構築から画面遷移についての記事を書きました。

今回は今まで構築した環境を使って、入力チェック(バリデーション)部分を手探りのままに作ってみたいと思います。

バリデーション処理をするためライブラリを探してみたところ、simple-react-validatorというなんとも組み込みやすそうなライブラリがありました。名前の通りsimpleであることに期待し、このライブラリを使ってチェック処理を実装したいと思います。

では、さっそく。

※ このブログは、わからないなりにとりあえず動かすことを目標にしております。本来Reactで推奨する使い方と異なる部分があるかもしれませんが、それはご愛嬌ということで大目にみてください。

なお以降の記述は、過去2回で作成しているものを使って進めていきます。まだその①、その②を見ていない方はそちらを先に見るのをおすすめします。

バリデーションに必要なものをインストール

まず、simple-react-validatorを使用するため、nodeのルートディレクトリ(app)ターミナルで以下コマンドを実行します。

# simple-react-validatorをインストール
# Next.jsのプロジェクトルートで実行すること
npm install simple-react-validator --save

login.tsxファイルを修正

前回までに作成したlogin.tsxファイルに対して修正します。全体を貼り付けると長くなるため、修正箇所に絞って追加箇所を説明したいと思います。

全体のプログラムファイルを確認したい場合はこちらをご確認ください。

その1:importの追加

インストールしたsimple-react-validatorをimportします。

import SimpleReactValidator from 'simple-react-validator';

その2:コンストラクタでvalidatorを生成

プロパティを定義し、validator機能を利用するため、インスタンスを生成します。

class Login extends React.Component<LoginProps, LoginState> {

  // prorperty
  private validator: any // 追加
 〜 省略 〜
  constructor(props: LoginProps) {
    super(props)
    this.state = {
     〜 省略 〜
    }
    this.validator = new SimpleReactValidator({ // 追加
      element: message => <div className="help is-danger">{message}</div> // 追加
    }) // 追加
  }

これでvalidatorを使う準備はOKです!!

SimpleReactValidatorは引数なしでも問題ないのですが、メッセージ表示するためのHTMLを合わせて出力したかったので、マニュアルを参考に設定しました。

その3:各入力項目のrule(チェック内容)を定義

SimpleReactValidatorのチェック定義はLaravelライクな定義となっています(個人的に最高な仕様)。

チェック定義は煩雑にならないように、プロパティとして今回は定義しました。

 〜 省略 〜
  // prorperty
  private validator: any

  private rules = new Map<string, string>([ // 追加
    ['email', 'required|email'], // 追加
    ['password', 'required'] // 追加
  ]) // 追加

  constructor(props: LoginProps) {
 〜 省略 〜

このように入力のname属性をキーにし、その入力項目のチェック定義を記載します。

その4:render()内のHTMLの修正

render()で返却するHTMLファイル内を修正します。

修正内容としては、

  • エラーメッセージ出力箇所の追加
  • 各入力項目(Input)にイベントを追加
  • classNameにクラス名の追加判定処理を追加

です。

補足をすると、各入力項目のイベントとしてonChangeonBlurのイベントを追加しています。

public render() {

    return (
      <Layout title="Login" isHeader={false} isFooter={false}>
     〜 省略 〜
            <article className="box is-rounded">
              <div className="field">
                <label className="label">Email</label>
                <p className="control has-icons-left">
                  <input className={`input ${this.isErrorInput('email', this.state.credentials.email) ? 'is-danger' : ''}`} type="email" name="email" value={this.state.credentials.email} onChange={this.handleCredentialsChange} onBlur={this.handleCredentialsBlur} placeholder="Email" />
                  <span className="icon is-small is-left">
                    <i className="fas fa-envelope"></i>
                  </span>
                </p>
                {this.getErrorMessage('email', this.state.credentials.email)}
              </div>
              <div className="field">
                <label className="label">Password</label>
                <p className="control has-icons-left">
                  <input className={`input ${this.isErrorInput('password', this.state.credentials.password) ? 'is-danger' : ''}`}  type="password" name="password" value={this.state.credentials.password} onChange={this.handleCredentialsChange} onBlur={this.handleCredentialsBlur} placeholder="Password" autoComplete="off" />
                  <span className="icon is-small is-left">
                    <i className="fas fa-lock"></i>
                  </span>
                </p>
                {this.getErrorMessage('password', this.state.credentials.password)}
              </div>
              <div className="field">
                <p className="">
                  <button className="button is-medium is-info is-fullwidth" onClick={this.handleLoginSubmit}>ログイン</button>
                </p>
              </div>
            </article>
     〜 省略 〜
    );
  }

その5:各種functionを作成(修正)

上述で定義したイベント発生時のfunctionを作成します。

SimpleReactValidatorの返却値を使って、メッセージ表示とInputのクラス名を追加する処理が欲しかったので、isErrorInput()getErrorMessage()という関数の追加と、onBlurイベント用の関数も追加しました。

〜 省略 〜
  handleCredentialsBlur = (e: React.ChangeEvent<HTMLInputElement>) => {
    let { credentials } = this.state
    credentials[e.target.name] = e.target.value
    if (!this.validator.fieldValid(e.target.name)) {
      // validation error
      this.validator.showMessageFor(e.target.name)
      this.forceUpdate();
    }
  }

  handleLoginSubmit = (e: React.MouseEvent<HTMLElement>) => {
    e.preventDefault()
    if (this.validator.allValid()) {
      alert('Input validation success.')
    } else {
      // alert('Input validation error.')
      // show message
      this.validator.showMessages()
      this.forceUpdate()
      const validErrors = this.validator.getErrorMessages()
      Object.keys(validErrors).forEach(validError => {
        // console.log(validError)
        // errorで何かしたい場合
      })

      return false;
    }
    this.setState({ isLoading: true })

    setTimeout(() => {
      this.setState({ isLoading: false })
      Router.push('/user')
      // Router.replace('/user')
    }, 1000)
  }

  // Inputのエラー判定
  isErrorInput = (name: string, value: string) => {
    // エラーメッセージの有無で判定
    return Boolean(this.getErrorMessage(name, value))
  }

  // エラーメッセージを表示
  getErrorMessage = (name: string, value: string) => {
    return this.validator.message(name, value, this.rules.get(name))
  }

  public render() {
  〜 省略 〜

肝となるSimpleReactValidatorの説明をすると、onBlurでfieldValid()を使って項目毎のチェック、allValid()を使ってsubmit時にすべての入力項目をチェックしています。

allValid()は引数に何も受け渡していないのに、どうやって入力項目を取得しているのが追いきれなかったのですが、console.log()にてvalidatorクラスのfieldsにキーが設定されていたので、恐らくコンストラクタで設定しているんだと思います(誰か知っている人いたら教えて……)。

ここまでを対応することで、こんな感じで表示が可能です。

さいごに

第2回からの差分はこちらからも確認できます。今回作ったプログラムはすべてGitHubに上がっておりますので、興味がある方は参照してみてください。

今回は入力チェック処理をなんとなく書いてみました。

説明が足りない部分もあるかと思いますので、Next.jsに興味がある方は、Next.jsのマニュアルとTypeScriptの情報を漁りながら、一度試してみてください!

次は何の勉強しようかな!

それではまた。やなさんでした。