react-final-form multiple field validation issue

62 views Asked by At

I created a custom form and field component using react-final-form in my nextjs app. I've written a custom validation function for the field, and I used these components for the username and email fields on my registration page. However, when a user enters only the username or email, both username and email validation start working. How can I make it validate only the username when the username is entered and only the email when the email is entered?

controlTypes.js

import { isEmail } from "validator";
import ErrorCode from "./errorMessage";
import usernameController from "./usernameController";

export const controllers = ["email", "username"];

export const getMessage = (code) => ErrorCode(code)?.message;

export const control = {
  email: async (value) => (isEmail(value) ? null : getMessage("1014")),
  username: async (value) => usernameController(value),
};

export default async function controlTypes(type, value) {
  const newValue = value || "";

  return control[type](newValue) || null;
}

customField.js

"use client";
import React, { useContext, useState } from "react";
import { Field as FinalField } from "react-final-form";
import controlTypes from "../../../../utils/controlTypes";
import styles from "../form.module.scss";
import Label from "../../label/label";
import Input from "../../input/input";
import Selectbox from "../../selectbox/selectbox";
import Message from "../../message/message";
import PropTypes from "prop-types";
import { FormContext } from "../form";


let timeOut;

export default function Field({
  label = null,
  input = null,
  selectbox = null,
  controlType = null,
  required = true,
  isRealtimeValidation,
  delay = 0,
  initialValue = "",

  ...props
}) {
  const { isPristineControl } = useContext(FormContext);
  const fieldValidate = isRealtimeValidation && controlType;

  const [inputValue, setInputValue] = useState(initialValue || "");

  async function validate(value) {
    const initialValue = value || "";
    const result = controlType ? controlTypes(controlType, initialValue) : null;

    return result;
  }

  async function handleChange(input) {
    clearTimeout(timeOut);
    timeOut = setTimeout(() => {
      input?.onChange(inputValue);
    }, delay);
  }

  return (
    <FinalField {...props} validate={validate} component="input">
      {({ input: fieldInput, meta }) => {
        const isValidate =
          (isPristineControl ? !meta.pristine : true) &&
          meta.visited &&
          !!meta.data &&
          !!fieldValidate;

        const validateStatus = fieldValidate && meta.valid;

        const isMessageView = isValidate && meta.touched;
        const message = isMessageView || meta.submitFailed ? meta.error : null;

        return (
          <div className={styles.formfield}>
            {!!label && (
              <Label {...label} htmlFor={props.name} isOptional={!required} />
            )}
            {!!input && (
              <Input
                {...input}
                {...fieldInput}
                value={inputValue}
                onChange={(e) => setInputValue(e.target.value)}
                onKeyDown={() => clearTimeout(timeOut)}
                onKeyUp={() => handleChange(fieldInput)}
                id={props.name}
                isRealtimeValidation={isValidate}
                realtimeValidationStatus={
                  meta.validating ? null : validateStatus
                }
              />
            )}

            {!!selectbox && <Selectbox {...selectbox} {...fieldInput} />}
            {!!message && <Message text={message} />}
          </div>
        );
      }}
    </FinalField>
  );
}

register.js

 <Form
          name={_content.form.name}
          isPristineControl
          initialValues={{
            email,
            username,
            [_content.form.additional.name]: additional,
          }}
          buttons={{
            primary: { ..._content.form.button },
          }}
          onSubmit={submitRegister}
        >
          <>
            {connectMessage && <Message text={connectMessage} />}
            <Form.Field
              controlType={_content.form.username.control_type}
              initialValue={username}
              input={{
                placeholder: _content.form.username.placeholder,
              }}
              isRealtimeValidation
              delay={_content.form.username.delay}
              label={{
                text: _content.form.username.label,
              }}
              name={_content.form.username.name}
            />
            {isVisibleEmailInput && (
              <Form.Field
                controlType={_content.form.email.control_type}
                initialValue={email}
                input={{
                  placeholder: _content.form.email.placeholder,
                }}
                isRealtimeValidation
                label={{
                  text: _content.form.email.label,
                }}
                name={_content.form.email.name}
              />
            )}
            <Form.Field
              name={_content.form.additional.name}
              label={{
                text: _content.form.additional.label,
              }}
              required={false}
              selectbox={{
                placeholder: _content.form.additional.placeholder,
                values: _content.form.additional.values,
              }}
            />
          </>
        </Form>

I separated the validation functions and added the validation function based on the incoming field name, but the issue still persists.

0

There are 0 answers