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.