Kohei Blog

夫・父親・医療系エンジニア

Zodで基本的なバリデーションをかけてみる

バリデーションライブラリ「Zod」の基本的な使い方についてまとめてみる。

Zodは、スキーマファーストなバリデーションライブラリ。 はじめにスキーマを定義して、パースすることでバリデーションをかけることができる。

CodeSandbox | zod

スキーマを定義する

varidation.ts

import { z } from "zod";

export type ValidationErrors = { [k: string]: string[] };

// schemaの定義
export const schema = z.object({
  text: z
    .string()
    .nonempty({ message: "入力は必須です。" })
    .min(5, "5文字以上で入力してください。")
});

export type TextInput = z.infer<typeof schema>;

// バリデーションに成功したら、データを返す
// 失敗すれば、エラーメッセージを返す
export const textValidate = (
  targetData: TextInput,
  success?: (result: TextInput) => void,
  fail?: (errors: ValidationErrors) => void
): void => {
  const result = schema.safeParse(targetData);

  if (result.success) {
    if (success) success(result.data);
  } else if (fail) fail(result.error.flatten().fieldErrors);
};

nonemptyは入力必須 minは最小文字数を定義できる 続けて、表示したいエラーメッセージを記述することができる。 上記のバリデーションを実行して、エラーメッセージを格納するロジックをカスタムフックに分離させる。

エラーメッセージを取得して格納する

hooks.ts

import { useCallback, useState } from "react";
import { textValidate, TextInput, ValidationErrors } from "./varidation";

export const useValidation = (): {
  validateErrors: ValidationErrors;
  doValidate: (textInput: TextInput, cb?: (result: TextInput) => void) => void;
} => {
// エラーメッセージの格納
  const [validateErrors, setValidateErrors] = useState<ValidationErrors>({
    text: []
  });

  const doValidate = useCallback(
    (textInput: TextInput, cb?: (result: TextInput) => void) => {
      textValidate(
        textInput,
        (result: TextInput) => {
          setValidateErrors({
            text: []
          });
          if (cb) cb(result);
        },
        (errors) => {
          setValidateErrors({
            ...errors
          });
        }
      );
    },
    []
  );

  return { validateErrors, doValidate };
};

エラーメッセージは、複数のフォームに対応するため、オブジェクトで持たせる

{
    email: []
		password: []
}

コンポーネントで使用

App.tsx

import React, { useState, useCallback } from "react";
import styled from "styled-components";
import { useValidation } from "./hooks";
import { TextInput } from "./varidation";
import TextInputField from "./TextInput";
import InputError from "./InputError";

import "./styles.css";

export type Props = {
  className?: string;
};

export const App: React.VFC<Props> = ({ className }) => {
  const [Input, setInput] = useState({ text: "" });
  const { validateErrors, doValidate } = useValidation();

  const changeInput = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      setInput({
        text: e.target.value
      });
    },
    [setInput]
  );

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    doValidate(Input, (result: TextInput) => console.log(result));
  };

  return (
    <div className={className}>
      <form onSubmit={handleSubmit}>
        <div className={`formInput`}>
          <InputError errorMessages={validateErrors.text}>
            <TextInputField
              name="text"
              placeholder="Text"
              onChange={changeInput}
              value={Input.text}
              isError={validateErrors.text.length > 0}
            />
          </InputError>
        </div>
        <button type="submit">Submit</button>
      </form>
    </div>
  );
};

export const StyledComponent = styled(App)`
  padding: 16px;

  form {
    margin: 0 auto;
  }
`;

export default StyledComponent;

設定したバリデーションエラーを格納して、エラーメッセージを表示することができた。 <InputError>コンポーネントは、単純にエラーメッセージを受け取って、表示するだけのコンポーネント

CodeSandbox | zod

参考リンク