我使用Chakra UI和react-hook-form尝试创建了一个表单

首先

这篇文章是2023年米洛戈斯圣诞活动的第十天发布的。
在这篇文章中,我想介绍如何利用Chakra UI和react-hook-form来创建表单。

本文涵盖了从表单基本创建步骤、添加验证处理,到使用图片创建单选按钮的详细解说。

React Hook Form 是什么

react-hook-form是一个用于在React应用程序中轻松创建表单并进行状态管理和验证的库。它旨在使用React Hooks有效地管理表单状态。

Please provide the content.

环境建设

请参考以下文章中的介绍。

 

添加包裝

    react-hook-form のインストール
npm install react-hook-form
    chakra-ui/icons のインストール
npm install @chakra-ui/icons

创造带有图像的单选按钮

在本部分中,我们将把常规的单选按钮替换为图像,并且能够获取所选图片的ID。以下是步骤:

准备图像

首先,准备所需的图像,并将它们放置在项目的public/images目录中。

.
├── README.md
├── next-env.d.ts
・・・
├── public
│   └── images
│       ├── image_1.png
│       ├── image_2.png
│       └── image_3.png
・・・

定义常数

在另一个文件(constants.ts)中定义所需的常量。
其中包括图像标题、路径和验证消息等。

export const TITLE_FIRST = 'タイトル1';
export const TITLE_SECOND = 'タイトル2';
export const TITLE_THIRD = 'タイトル3';
export const IMAGE_PATH_FIRST = '/images/image_1.png';
export const IMAGE_PATH_SECOND = '/images/image_2.png';
export const IMAGE_PATH_THIRD = '/images/image_3.png';
export const IMAGE_ID_VALIDATION_MESSAGE = '画像を選択してください。';
export const MESSAGE_FIELD_VALIDATION_MESSAGE =
  'メッセージを入力してください。';
export const MESSAGE_FIELD_MAX_LENGTH = 100;
export const MESSAGE_FIELD_MAX_LENGTH_VALIDATION_MESSAGE =
  'メッセージは100文字以内で入力してください。';
export const SUBMIT_BUTTON_TEXT = '送信';

以图像形式展示的列表

我們將創建一個用於顯示圖像列表的組件。

import { Box, Flex, Heading, Image, Text, VStack } from "@chakra-ui/react";
import React from "react";
import * as FormConstants from "./constants";

const IndexPage = () => {
  const images = [
    {
      id: 1, // 画像ID
      title: FormConstants.TITLE_FIRST, // 画像タイトル
      src: FormConstants.IMAGE_PATH_FIRST, // 画像パス
    },
    {
      id: 2,
      title: FormConstants.TITLE_SECOND,
      src: FormConstants.IMAGE_PATH_SECOND,
    },
    {
      id: 3,
      title: FormConstants.TITLE_THIRD,
      src: FormConstants.IMAGE_PATH_THIRD,
    },
  ];

  return (
    <>
      <Flex alignItems="center" justifyContent="center">
        <VStack spacing="4" align="start" padding="0">
          <VStack align="start">
            <Heading size="lg">画像の選択</Heading>
            <Heading fontSize="14px">
              画像をクリックすると対象の画像IDを取得できます
            </Heading>
          </VStack>
          {images.map((image, index) => (
            <React.Fragment key={index}>
              <Text>{image.title}</Text>
              <Box
                position="relative"
                key={image.id}
                borderRadius="md"
                marginBottom={index === images.length - 1 ? "30px" : "0"}
              >
                <Image src={image.src} alt={`Image ${image.id}`} width="100%" />{" "}
              </Box>
            </React.Fragment>
          ))}
        </VStack>
      </Flex>
    </>
  );
};

在这个组件中,我们根据images数组中包含的图像信息进行图片的列表显示。
对于每个图片,我们都分配了一个图像ID。

如果屏幕显示如下内容,就是OK了。

image.png
Flex 组件

Flex组件通过操作父元素和子元素,提供灵活且响应式的布局。

alignItems=”center”

alignItemsプロパティは、子要素を垂直方向に中央揃えに設定しています。

justifyContent=”center”

justifyContentプロパティは、子要素を水平方向に中央揃えに設定しています。

垂直堆栈组件

VStack组件是Chakra UI的一个用于将子元素垂直方向(纵向)排列的组件。

spacing=”4″:

spacingプロパティは、子要素間の垂直方向の間隔を指定します。この例では、4の間隔が設定されています。

align=”start”:

alignプロパティは、子要素の水平方向の配置を指定します。”start”は左寄せを意味します。子要素は左端に揃えられます。

盒子组件

这个被用来包裹其他元素和组件,以设置样式和属性。

position=”relative

要素に対して相対的な位置を指定します。

borderRadius=”md

角を丸くします。

marginBottom={index === images.length – 1 ? “30px” : “0”}

三項演算子が使用されています。画像が配列内で最後の要素である場合、マージンは”30px”に設定されます。

React.Fragment (React.Fragment) 是一个 React 组件,用于包裹多个子元素而不会生成额外的容器元素。

这是用来将多个元素组合在一起而不生成可视化元素的工具。
它是一个没有任何显示内容的空组件,主要用于包裹多个元素。

获取所选图片ID。

当用户点击图像时,要获得该图像的ID,请实施以下步骤。

    画像をクリックする関数の指定
images.map((image, index) => (
  <React.Fragment key={index}>
    <Text>{image.title}</Text>
    <Box
      position="relative"
      key={image.id}
      borderRadius="md"
      onClick={() => {
        // クリックされた際にsetSelectedImageId関数を呼び出します
        setSelectedImageId(image.id);
      }}
      marginBottom={index === images.length - 1 ? "30px" : "0"}
    >
      <Image src={image.src} alt={`Image ${image.id}`} width="100%" />
    </Box>
  </React.Fragment>
));
    選択された画像IDを取得する状態管理の初期化
import React, { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';

・・・

// selectedImageIdを初期化
const [selectedImageId, setSelectedImageId] = useState(null as number | null);
// コンソールに出力
console.log("選択した画像ID", selectedImageId);

当用户点击图像时,将调用setSelectedImageId函数,并将选定图像的ID设置为selectedImageId状态。这个值也会被输出到控制台上,以便验证所选择的图像ID。

使用useState钩子

useState钩子接受初始状态作为参数,并返回当前状态和更新状态的函数。

    • const [selectedImageId, setSelectedImageId] = useState(null as number | null);

useStateフックを使用して、selectedImageIdという状態変数を宣言しています。これは現在選択されている画像のIDを保持します。

useState 是 React 的一个钩子,用于在组件中添加 state 变量。

为所选择的图像添加一个勾选图标

給選擇的圖像添加勾選圖示的程式碼如下所示。

images.map((image, index) => (
  <Box
    key={image.id}
    position="relative"
    borderRadius="md"
    onClick={() => {
      setSelectedImageId(image.id);
    }}
    marginBottom={index === images.length - 1 ? "30px" : "0"}
  >
    <Text>{image.title}</Text>
    <Image
      src={image.src}
      alt={`Image ${image.id}`}
      width="100%"
      // 三項演算子を使用して、選択された画像かどうかに応じてopacityを変更
      opacity={selectedImageId === image.id ? 1 : 0.6}
    />
    // 選択された画像にCheckIconを表示
    {selectedImageId === image.id && (
      <CheckIcon
        position="absolute"
        top="5px"
        right="5px"
        w={6}
        h={6}
        color="green.400"
      />
    )}
  </Box>
));

在此代码中,使用三目运算符根据是否选择的图像来切换不透明度属性。
此外,对于选择的图像,显示了CheckIcon。
这样,当用户点击图像时,选择的图像将显示一个检查图标。

opacity={selectedImageId === image.id ? 1 : 0.6}

選択された画像の場合はopacityを1(不透明)にし、それ以外の場合は0.6(60%透明)にします。

{selectedImageId === image.id && ( ・・・)}

選択された画像の場合にのみ CheckIcon を表示します。

只要显示出以下内容即可。

image.png

创建表单

在这个部分,我们将构建一个通过按下按钮来获取消息和图片ID的表格。
以下是该代码。

const {
  register,
  handleSubmit,
  setValue,
} = useForm();

// useEffectを使用して、selectedImageIdが変更されたときにフォームの値の更新
useEffect(() => {
  setValue('imageId', selectedImageId);
}, [selectedImageId, setValue]);

const onSubmit = (data) => {
  const imageId = data.imageId;
  const message = data.message;

  // 取得した画像IDとメッセージをコンソールに出力
  console.log(message);
  console.log(imageId);
};

return (
  <>
    ・・・
    <Flex alignItems="center" justifyContent="center">
      {/* フォームの送信時に実行される関数を指定 */}
      <form onSubmit={handleSubmit(onSubmit)}>
        <VStack align="start" marginBottom="20px">
          <Heading size="lg">メッセージの入力</Heading>
          <Heading fontSize="14px">入力された内容を取得します</Heading>
        </VStack>
        <Textarea
          // react-hook-form の register メソッドを使用してフォームフィールドを登録
          {...register('message')} 
          height="200px"
          border="2px solid black"
          marginBottom="20px"
        />
        <Button
          type="submit"
          width="100%"
          backgroundColor="black"
          color="white"
        >    
          {FormConstants.SUBMIT_BUTTON_TEXT}
        </Button>
      </form>
    </Flex>
  </>
);

在这段代码中,我们使用react-hook-form来管理表单的状态,并利用useEffect钩子来在selectedImageId发生变化时更新表单的值。
此外,当表单被提交时,指定的函数会被执行,并将消息和图片ID输出到控制台上。

如果当您按下按钮时,出现以下表示的话,那就是OK的。

image.png
使用 useForm 钩子

useForm是react-hook-form库提供的一个钩子,用于在React应用程序中方便地管理表单状态和验证等。

    • const { setValue } = useForm();

setValueは、react-hook-formライブラリで提供されるuseFormフックから取得できるメソッドの一つです。
第一引数に入れた変数に第二引数に入れた値をセットする関数となります。

useForm
useForm是一个用于方便管理表单的自定义钩子。它可以接受一个可选的对象作为参数。以下示例展示了它的所有属性以及它们的默认值。

useEffect 钩子

useEffect 在 React 组件渲染完成后用于执行异步操作或副作用。

    • useEffect(() => {

 

    • setValue(“imageId”, selectedImageId);

 

    • }, [selectedImageId, setValue]);

このuseEffectブロックは、selectedImageIdの値が変更されたときに実行されます。そして、setValue(“imageId”, selectedImageId)を実行しています。

useEffect是React钩子,用于将组件与外部系统同步。

创建验证

这段代码展示了对表单中的单选按钮和消息字段进行验证的创建。

单选按钮的有效性验证

在这段代码中,我们对单选按钮的选择进行了验证,确保必须做出选择。

<FormControl isInvalid={!!errors.imageId}>
  {/* react-hook-form の Controller を使用してフォームフィールドを制御 */}
  <Controller
    name="imageId"
    control={control}
    defaultValue=""
    rules={{
      required:
        FormConstants.IMAGE_ID_VALIDATION_MESSAGE,
    }}
    render={({ field }) => (
      <input
        type="hidden"
        value={field.value || ''}
        onChange={field.onChange}
      />
    )}
  />
  {/* もしエラーメッセージがあれば表示 */}
  {typeof errors.imageId?.message === 'string' && (
    <FormErrorMessage>{errors.imageId.message}</FormErrorMessage>
  )}
</FormControl>

该代码要求在表单中选择图片单选按钮是必须的,如果未选择,则显示错误消息。

image.png
消息字段验证

在这段代码中,我们实现了对消息字段的验证。

<FormControl isInvalid={!!errors.message} width='500px'>
  {/* エラーメッセージが存在する場合に表示 */}
  {typeof errors.message?.message === 'string' && (
    <FormErrorMessage>{errors.message.message}</FormErrorMessage>
  )}
  <VStack align="start" marginBottom="20px">
    <Heading size="lg">メッセージの入力</Heading>
    <Heading fontSize="14px">入力された内容を取得します</Heading>
  </VStack>
  <Textarea
    {...register('message', {
      // 必須入力のバリデーションルール
      required:
        FormConstants.MESSAGE_FIELD_VALIDATION_MESSAGE,
      // 最大文字数のバリデーションルール
      maxLength: {
        value: FormConstants.MESSAGE_FIELD_MAX_LENGTH,
        message:
          FormConstants.MESSAGE_FIELD_MAX_LENGTH_VALIDATION_MESSAGE,
      },
    })}
    height="200px"
    border="2px solid black"
    marginBottom="20px"
  />
  <Button
    type="submit"
    width="100%"
    backgroundColor="black"
    color="white"
  >
    {FormConstants.SUBMIT_BUTTON_TEXT}
  </Button>
</FormControl>

如果消息是必需的并且设置了最大字符数,该代码将对其进行验证。
如果存在错误,则显示错误消息。

image.png
表單控制元件

FormControl组件是Chakra UI库的一部分,作为控制表单内控件的容器而存在。

    • isInvalid

FormControlには、フォームのエラー状態を示すためのisInvalidプロパティがあります。
!!errors.imageIdは、errors.imageIdが存在する場合にtrueになり、エラーがあることを示します。これにより、エラーの有無に基づいてフォームコントロールのスタイリングが変更されることがあります。

控制器组件

Controller组件是react-hook-form库的一部分,用于有效地控制表单内每个字段。

    • rules

フィールドに対するバリデーションルールを設定します。

control

useFormフックから提供されるフォームの制御オブジェクトです。

render

Controller内で描画される要素を指定します。この例では隠しフィールドを描画しています。

文本区域组件

Textarea组件提供了一个多行文本输入字段,供用户输入消息使用。

    • register

react-hook-formライブラリのregisterメソッドを使用して、Textareaをフォームに登録します。これにより、フォームの状態と連携し、バリデーションやフォームデータの管理が可能になります。

…register(‘message’, {…})

messageフィールドのバリデーションルールが設定されています。この例では、requiredルールとmaxLengthルールが指定されています。

整个源代码

最后,将之前的源代码进行整理。

目录结构

.
├── README.md
├── next-env.d.ts
├── node_modules
├── package-lock.json
├── package.json
├── pages
│   ├── _app.tsx
│   ├── constants.ts
│   └── index.tsx
├── public
├── tsconfig.json
└── utils

定义一个常数

export const TITLE_FIRST = 'タイトル1';
export const TITLE_SECOND = 'タイトル2';
export const TITLE_THIRD = 'タイトル3';
export const IMAGE_PATH_FIRST = '/images/image_1.png';
export const IMAGE_PATH_SECOND = '/images/image_2.png';
export const IMAGE_PATH_THIRD = '/images/image_3.png';
export const IMAGE_ID_VALIDATION_MESSAGE = '画像を選択してください。';
export const MESSAGE_FIELD_VALIDATION_MESSAGE =
  'メッセージを入力してください。';
export const MESSAGE_FIELD_MAX_LENGTH = 100;
export const MESSAGE_FIELD_MAX_LENGTH_VALIDATION_MESSAGE =
  'メッセージは100文字以内で入力してください。';
export const SUBMIT_BUTTON_TEXT = '送信';

创建表单

import { CheckIcon } from '@chakra-ui/icons';
import {
  Box,
  Button,
  Flex,
  FormControl,
  FormErrorMessage,
  Heading,
  Image,
  Text,
  Textarea,
  VStack
} from '@chakra-ui/react';
import React, { useEffect, useState } from 'react';
import { Controller, useForm } from 'react-hook-form';
import * as FormConstants from './constants';

const IndexPage = () => {
  const images = [
    {
      id: 1,
      title: FormConstants.TITLE_FIRST,
      src: FormConstants.IMAGE_PATH_FIRST,
    },
    {
      id: 2,
      title: FormConstants.TITLE_SECOND,
      src: FormConstants.IMAGE_PATH_SECOND,
    },
    {
      id: 3,
      title: FormConstants.TITLE_THIRD,
      src: FormConstants.IMAGE_PATH_THIRD,
    },
  ];
  const [selectedImageId, setSelectedImageId] = useState(null as number | null);
  console.log("選択した画像ID", selectedImageId);

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

  useEffect(() => {
    setValue('imageId', selectedImageId);
  }, [selectedImageId, setValue]);

  const onSubmit = (data) => {
    const imageId = data.imageId;
    const message = data.message;

    console.log("取得した画像ID", imageId);
    console.log("取得したメッセージ", message);
  };

  return (
    <>
      <Flex alignItems="center" justifyContent="center">
        <VStack spacing="4" align="start" padding="0">
          <VStack align="start">
            <Heading size="lg">画像の選択</Heading>
            <Heading fontSize="14px">画像をクリックすると対象の画像IDを取得できます</Heading>
          </VStack>
          <FormControl isInvalid={!!errors.imageId}>
            <Controller
              name="imageId"
              control={control}
              defaultValue=""
              rules={{
                required:
                  FormConstants.IMAGE_ID_VALIDATION_MESSAGE,
              }}
              render={({ field }) => (
                <input
                  type="hidden"
                  value={field.value || ''}
                  onChange={field.onChange}
                />
              )}
            />
            {typeof errors.imageId?.message === 'string' && (
              <FormErrorMessage>{errors.imageId.message}</FormErrorMessage>
            )}
          </FormControl>
          {images.map((image, index) => (
            <React.Fragment key={index}>
              <Text>{image.title}</Text>
              <Box
                position="relative"
                key={image.id}
                borderRadius="md"
                onClick={() => {
                  setSelectedImageId(image.id);
                }}
                marginBottom={index === images.length - 1 ? '30px' : '0'}
              >
                <Image
                  src={image.src}
                  alt={`Image ${image.id}`}
                  width="100%"
                  opacity={selectedImageId === image.id ? 1 : 0.6}
                />{' '}
                {selectedImageId === image.id && (
                  <CheckIcon
                    position="absolute"
                    top="5px"
                    right="5px"
                    w={6}
                    h={6}
                    color="green.400"
                  />
                )}{' '}
              </Box>
            </React.Fragment>
          ))}
        </VStack>
      </Flex>
      <Flex alignItems="center" justifyContent="center">
        <form onSubmit={handleSubmit(onSubmit)}>
          <FormControl isInvalid={!!errors.message} width='500px'>
            {typeof errors.message?.message === 'string' && (
              <FormErrorMessage>{errors.message.message}</FormErrorMessage>
            )}
            <VStack align="start" marginBottom="20px">
              <Heading size="lg">メッセージの入力</Heading>
              <Heading fontSize="14px">入力された内容を取得します</Heading>
            </VStack>
            <Textarea
              {...register('message', {
                required:
                  FormConstants.MESSAGE_FIELD_VALIDATION_MESSAGE,
                maxLength: {
                  value: FormConstants.MESSAGE_FIELD_MAX_LENGTH,
                  message:
                    FormConstants.MESSAGE_FIELD_MAX_LENGTH_VALIDATION_MESSAGE,
                },
              })}
              height="200px"
              border="2px solid black"
              marginBottom="20px"
            />
            <Button
              type="submit"
              width="100%"
              backgroundColor="black"
              color="white"
            >
              {FormConstants.SUBMIT_BUTTON_TEXT}
            </Button>
          </FormControl>
        </form>
      </Flex>
    </>
  );
};

export default IndexPage;

最后

通过这篇文章,我对构建表单所需的组件和库有了更深入的理解,并获取了新的知识。

Chakra UI是一个方便的工具,它可以轻松构建易于阅读和使用的用户界面,并且能够有效而舒适地推进开发过程。

如果您愿意,我希望您能参考一下。

广告
将在 10 秒后关闭
bannerAds