Yup Validation causing errors - reactjs

I'm trying to validate my forms on React using yup and useForm, I've followed every step of the tutorial but I am still receiving errors. I am receiving 'TypeError: Cannot read property 'firstName' of undefined'. This is odd because I have already named it in the input value. How can I fix this?
P.s I have tried using {...register} and ref={register} don't seem to matter.
import React from 'react'
import { useForm } from "react-hook-form";
import { yupResolver } from "#hookform/resolvers/yup";
import * as yup from "yup";
const schema = yup.object().shape({
firstName: yup.string().required("First Name should be required please"),
lastName: yup.string().required(),
email: yup.string().email().required(),
});
const Submission = () => {
const { register, handleSubmit, errors } = useForm({
resolver: yupResolver(schema),
});
const submitForm = (data) => {
console.log(data);
};
return (
<div className="Form">
<div className="title">Sign Up</div>
<div className="inputs">
<form onSubmit={handleSubmit(submitForm)}>
<input
type="text"
name="firstName"
{...register('firstName')}
placeholder="First Name..."
/>
<p> {errors.firstName?.message} </p>
<input
type="text"
name="lastName"
placeholder="Last Name..."
ref={register}
/>
<p> {errors.lastName?.message} </p>
<input
type="text"
name="email"
placeholder="Email..."
ref={register}
/>
<p> {errors.email?.message} </p>
<input type="submit" id="submit" />
</form>
</div>
</div>
);
}
export default Submission;
The exact error I am recieving looks like this:

Turns out I'm still using the outdated version of the library.
From this,
<input type="text"name="email"placeholder="Email..."ref={register}/>
const { register, handleSubmit, errors } = useForm({
resolver: yupResolver(schema),
});
To this
{...register("email", { required: "Email is Required"})};
const { register,handleSubmit, formState: { errors },} = useForm();

Related

Problem with using if statement in react-hook-form

Validation in react-hook-form using if, else statement return only when return value is true. But if the statement is false it returns nothing.
import * as React from "react";
import * as ReactDOM from "react-dom";
import { useForm } from "react-hook-form";
import "./styles.css";
const App = () => {
const {
register,
handleSubmit,
getValues,
formState: { isValid }
} = useForm({
mode: "onChange",
defaultValues: {
firstName: "",
lastName: ""
}
});
const onSubmit = () => {
if (isValid) {
console.log("valid");
} else {
console.log("invalid");
}
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label>First Name</label>
<input type="text" {...register("firstName", { required: true })} />
<label>Last Name</label>
<input type="text" {...register("lastName", { required: true })} />
<input type="submit" />
</form>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
I don't know what's wrong. Can someone help please.
Here's the code from codesandbox
I don't think anything is wrong with the code. If you only enter the last name and hit submit, it highlights the first name box. And same for the opposite. If you only enter the first name and hit submit, it highlights the second box. This seems to me like a default behavior from the react hook form. Here you can see I added a useEffect to it to print out the validity. When the form is valid you can see it shows correctly.
This seems to be a feature from the react hook form to prevent submitting if it is not valid.
Edit: Look into their import { ErrorMessage } from "#hookform/error-message"; system they have in place. And Here is an updated code sandbox with error handling
Your form wont submit unless required fields are entered on inputs:
const onSubmit = () => {
if (!isValid) {
console.log("valid");
} else {
console.log("invalid");
}
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label>First Name</label>
<input type="text" {...register("firstName")} />
<label>Last Name</label>
<input type="text" {...register("lastName")} />
<input type="submit" />
</form>
);
};

'react-hook-form' form types

I created this form using react hooks form:
import React from "react";
import ReactDOM from "react-dom";
import { useForm, SubmitHandler } from "react-hook-form";
import "./styles.css";
function App() {
type FormValues = {
firstName: string;
lastName: string;
email: string;
};
const { register, handleSubmit } = useForm<FormValues>();
const onSubmit: SubmitHandler<FormValues> = (data) =>
alert(JSON.stringify(data));
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<label>First Name</label>
<input {...register("firstName")} />
</div>
<div>
<label>Last Name</label>
<input {...register("lastName")} />
</div>
<div>
<label htmlFor="email">Email</label>
<input type="email" {...register("email")} />
</div>
<input type="submit" />
</form>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Now typescript enforce me to register input's name ({...register("name")}) only using the keys from FormValues. If i will remove email: string; from FormValues then i will get a TS error on <input type="email" {...register("email")} /> with the next message:
Argument of type '"email"' is not assignable to parameter of type '"firstName" | "lastName"'.ts(2345)
Question: How to get rig of this issue and to make the inputs name independently from FormValues?demo: https://codesandbox.io/s/react-hook-form-get-started-ts-forked-wfzjyr?file=/src/index.tsx:735-780
Only solution I see is this:
function App() {
type FormValues = {
firstName: string;
lastName: string;
// email: string;
};
const { register, handleSubmit } = useForm<FormValues>();
const onSubmit: SubmitHandler<FormValues> = (data) =>
alert(JSON.stringify(data));
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<label>First Name</label>
<input {...register("firstName")} />
</div>
<div>
<label>Last Name</label>
<input {...register("lastName")} />
</div>
<div>
<label htmlFor="email">Email</label>
<input type="email" {...(register as UseFormRegister<any>)("email")} />
</div>
<input type="submit" />
</form>
);
}
Basically, you have to type your register as any to not trigger typescript error <input type="email" {...(register as UseFormRegister<any>)("email")} />.
Can I ask what you want to achieve exactly, because what you're asking here goes against typescript and react-hook-form principles.
Why would you want to have a form where one field is registered but not treated as one of the fields by typescript ? There might be a much better solution to what you want to do.

Value type error message displaying instead of required error message on empty input submission

I'm using react-hook-form and yup together and for some reason it's displaying the wrong error message.
When the phone number input is left empty on submitting the form, it displays the typeError error message instead of the 'required' error message which I can't understand why that's happening since the input field is blank and there's no value to determine that it's of type 'number' or not.
import { yupResolver } from "#hookform/resolvers/yup";
import { useForm } from "react-hook-form";
import * as yup from "yup";
const schema = yup.object().shape({
name: yup.string().required("Field is required*"),
email: yup.string().email().required("Field is required*"),
phoneNumber: yup
.number()
.required("Field is required*") //this message should be displayed instead of typeError message
.typeError("Value must be a number*"),
message: yup.string().required("Please provide your message*")
});
export default function App() {
const {
reset,
register,
handleSubmit,
formState: { errors }
} = useForm({ resolver: yupResolver(schema) });
const submitForm = (data) => {
console.log(data);
reset();
};
return (
<div className="App">
<form className="contact-form" onSubmit={handleSubmit(submitForm)}>
<h2>Send Us a Message </h2>
<p>
<label for="name">Name</label>
<input name="name" type="text" {...register("name")} />
<p className="error-msg"> {errors.name?.message} </p>
</p>
<p>
<label for="email">Email</label>
<input name="email" type="text" {...register("email")} />
<p className="error-msg"> {errors.email?.message} </p>
</p>
<p>
<label for="phoneNumber">Phone</label>
<input
name="phoneNumber"
type="text"
{...register("phoneNumber")}
/>
</p>
<p className="error-msg"> {errors.phoneNumber?.message} </p>
<input type="submit" id="submit" />
</form>
</div>
);
Because Yup receives an empty string instead of a number, it thinks you're passing the wrong type. You need to tell RHF to return undefined if the input is empty:
<input
name="phoneNumber"
type="text"
{...register("phoneNumber", {
setValueAs: (v) => {
return v === "" ? undefined : parseInt(v, 10);
}
})}

Simple react form validation with yup

I'm trying to learn form validation with yup and react-hook-form and, ideally want to set the validation to also require at least one of the two different-named radio options, but first understand why the isValid variable is false.
Here's my code:
import React from "react";
import { useForm } from "react-hook-form";
import * as yup from "yup"
const App = () => {
const { register, handleSubmit, watch, errors } = useForm();
const onSubmit = async data => {
console.log('form data', data);
const isValid = await schema.isValid();
console.log('isValid', isValid);
}
return (
<>
{console.log('errors', errors)}
<form onSubmit={handleSubmit(onSubmit)}>
<input name="name" defaultValue="test" ref={register} />
<input name="nameTwo" ref={register({ required: true })} />
{errors.exampleRequired && <span>This field is required</span>}
<label>
<input
name="test"
type="radio"
ref={register}
value="test"
/>
</label>
<label>
<input
name="testTwo"
type="radio"
ref={register}
value="testTwo"
/>
</label>
<input type="submit" />
</form>
</>
);
}
const schema = yup.object().shape({
name: yup.string(),
nameTwo: yup.string().required(),
test: yup.string(),
testTwo: yup.string()
})
export default App
So the schema defines only one field (nameTwo) as required, but even when filling in that field it still is false. Why is isValid not true? And how is it best to go about making either one of the two radio buttons (test or testTwo) required?
Stackblitz demo: https://stackblitz.com/edit/react-93vahd
Thank you
The isValid property is available in the formState object returned from the useForm hook.
You'll notice that onSubmit is not called unless the Yup validation passes. Therefore isValid will always be true when checking inside the onSubmit callback.
import React from 'react';
import { useForm } from 'react-hook-form';
import * as yup from 'yup';
const App = () => {
const {
register,
handleSubmit,
errors,
formState: { isValid }
} = useForm();
const onSubmit = async data => {
console.log('form data', data);
};
return (
<>
{console.log('isValid', isValid)}
{console.log('errors', errors)}
<form onSubmit={handleSubmit(onSubmit)}>
<input name="name" defaultValue="test" ref={register} />
<input name="nameTwo" ref={register({ required: true })} />
{errors.exampleRequired && <span>This field is required</span>}
<label>
<input name="test" type="radio" ref={register} value="test" />
</label>
<label>
<input name="testTwo" type="radio" ref={register} value="testTwo" />
</label>
<input type="submit" />
</form>
</>
);
};
const schema = yup.object().shape({
name: yup.string(),
nameTwo: yup.string().required(),
test: yup.string(),
testTwo: yup.string()
});
export default App;

Formik 'initialValues' Typescript Errors in React.js with hooks

My react.js registration form works fine, then decided to apply Formik to it and got stuck on 2 issues: I keep getting a TS error message on 'initialValues' from within the 'Formik' tag. Also, after switching 'input' to 'Field' tag, parameter function 'e' from onChange also displays an underline ts error. Same issue with style property from 'ErrorMessage' tag.
I am only trying to apply a simple Formik approach to my form. Any suggestions please?
import React, { useState, FormEvent, useRef } from 'react';
import { Link, withRouter, useHistory } from 'react-router-dom';
import api from '../services/api';
import PrimaryButton from "../components/PrimaryButton";
import * as Yup from 'yup';
import { useForm } from 'react-hook-form';
import { Formik, Field, Form, ErrorMessage } from 'formik';
const userSchema = Yup.object().shape({
name: Yup.string().required('Nome obrigatório.').max(60),
email: Yup.string().required('Email obrigatório'),
password: Yup.string().required('Senha obrigatória').min(4, 'Ops, curta demais.').max(10, 'Ops, longa demais.'),
})
function Register() {
const history = useHistory();
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [firstPassword, setFirstPassword] = useState('');
const [password, setPassword] = useState('');
const { register, handleSubmit, errors, getValues } = useForm();
// const inputPassword = useRef(null)
async function handleAddDev(e: FormEvent) {
// e.preventDefault();
let formData = {
name,
email,
password,
}
const isValid = await userSchema.isValid(formData);
console.log(isValid);
const response = await api.post('/users/new', formData)
console.log(response.data);
alert('Cadastro efetuado com sucesso.')
setName('');
setEmail('');
setFirstPassword('');
setPassword('');
history.push('/')
}
const myValues = {
name: "",
email: "",
firstPassword: "",
password: "",
}
return (
<>
<div id="register">
<strong>Cadastrar Usuário</strong>
<Formik
initialValues={myValues}
validationSchema={userSchema}
onSubmit={handleAddDev}>
<Form >
<div className="input-block">
<label htmlFor="name">Nome</label>
<Field
name="name"
id="name"
type="text"
placeholder= "Seu nome"
value={name}
onChange={e => setName(e.target.value)}
required />
<ErrorMessage
name="name"
component="div"
className="invalid-feedback"
style={{ color:"red", fontWeight:"bold"}}/>
</div>
<div className="input-block">
<label htmlFor="email">E-mail</label>
<Field
name="email"
id="email"
type="email"
placeholder= "Seu email"
value={email}
onChange={e => setEmail(e.target.value)}
/>
<ErrorMessage
name="email"
component="div"
className="invalid-feedback"
style={{ color:"red", fontWeight:"bold"}}/>
</div>
<div className="input-block">
<label htmlFor="email">Senha</label>
<Field
name="password"
id="password"
type="text"
placeholder= "Senha"
value={firstPassword}
onChange={e => setFirstPassword(e.target.value)}
ref={register}/>
<ErrorMessage
name="password"
component="div"
className="invalid-feedback"
style={{ color:"red", fontWeight:"bold"}}/>
</div>
<div className="input-block">
<label htmlFor="email">Confirmar Senha</label>
<Field
name="passwordConfirmation"
id="password"
type="text"
placeholder= "Confirme sua sehna"
ref={register({
validate: {
passwordEqual: value => (value === getValues().password) || 'Password confirmation error!',
}
})}
value={password}
onChange={e => setPassword(e.target.value)}
required />
<ErrorMessage
name="name"
component="div"
className="invalid-feedback"
style={{ color:"red", fontWeight:"bold"}}/>
{errors.passwordConfirmation && <p>{errors.passwordConfirmation.message}</p>}
</div>
<PrimaryButton type="submit">Cadastrar</PrimaryButton>
</Form>
</Formik>
<button><Link to='/'></Link> </button>
</div>
</>
)
}
export default withRouter(Register);
If your file extesion is tsx, you need to define a interface for the form values:
interface MyFormValues {
name: string,
email: string,
firstPassword: string,
password: string,
}
const myValues: MyFormValues = {
name: "",
email: "",
firstPassword: "",
password: "",
}
For the field error, the value and onChange is handled by formic so you don't need them anymore.
https://formik.org/docs/guides/typescript
const myValues = {
name: "",
email: "",
firstPassword: "",
password: "",
}
You do not have field name 'firstPassword'. Instead you have name="passwordConfirmation". initial values must match the field names.
Regarding second issue:
onChange = {(e: React.FormEvent<HTMLInputElement>)=>setFirstPassword(e.target.value)}

Resources