When using trigger() on react hook form I can't read the errors object on first attempt. I think this is because the object populates on a subsequent render.
Here is full working example: https://codesandbox.io/s/crimson-firefly-f8ulg7?file=/src/App.tsx
You can see the first time you click submit it logs an empty object and does not set focus. If you click it again then it will work as intended.
Here is example form code:
import "./styles.css";
import classNames from "classnames";
import { useForm } from "react-hook-form";
export default function App() {
const {
register,
handleSubmit,
trigger,
watch,
formState: { errors }
} = useForm();
const onSubmit = (data: any) => console.log(data);
return (
<div className="App">
<div className="form">
<form onSubmit={handleSubmit(onSubmit)}>
<div className={"row"}>
<div className="label">Name</div>
<div
className={classNames({
input: true,
error: errors?.name !== undefined
})}
>
<input {...register("name", { required: "Name is required" })} />
</div>
</div>
<div className="row">
<div className="label">Company</div>
<div
className={classNames({
input: true,
error: errors?.company !== undefined
})}
>
<input
{...register("company", { required: "Company is required" })}
/>
</div>
</div>
<div className="row">
<div className="label">Tel</div>
<div
className={classNames({
input: true,
error: errors?.tel !== undefined
})}
>
<input
{...register("tel", { required: "Telephone is required" })}
/>
</div>
</div>
<div className="row">
<div className="label">Mobile</div>
<div
className={classNames({
input: true,
error: errors?.mobile !== undefined
})}
>
<input
{...register("mobile", { required: "Mobile is required" })}
/>
</div>
</div>
<div className="row">
<div className="label">Email</div>
<div
className={classNames({
input: true,
error: errors?.email !== undefined
})}
>
<input
{...register("email", { required: "Email is required" })}
/>
</div>
</div>
</form>
</div>
<div className="button">
<a
href="#"
onClick={() => {
trigger().then((res) => {
if (res) {
handleSubmit(onSubmit)();
} else {
let elem = errors[Object.keys(errors)[0]]
?.ref as HTMLInputElement;
elem?.focus();
// setTimeout(() => {
// (errors[Object.keys(errors)[0]]?.ref as HTMLInputElement).focus();
// }, 10);
// (errors[Object.keys(errors)[0]]?.ref as HTMLInputElement).focus();
console.log(errors);
}
});
}}
>
Submit
</a>
</div>
</div>
);
}
I tried using a timeout but it's still empty on the first attempt.
How do I trigger the forms validation and run code based on the results of the validation?
I want to know the errored fields but also have the ref that is included inside the error object.
After reviewing the comment from the other answer you can access the error object by using the getFieldState in the useForm hook and then calling it in your trigger console.log('error object', getFieldState('name').error). You can also just call console.log('field state', getFieldState('name')) to get more info for that field, including the error object.
I forked your sandbox with the updated code.
const {
register,
handleSubmit,
trigger,
getFieldState,
watch,
formState: {
errors
}
} = useForm();
<a
href = "#"
onClick = {
() => {
trigger().then((res) => {
if (res) {
handleSubmit(onSubmit)();
} else {
let elem = errors[Object.keys(errors)[0]] ?
.ref as HTMLInputElement;
elem ? .focus();
// setTimeout(() => {
// (errors[Object.keys(errors)[0]]?.ref as HTMLInputElement).focus();
// }, 10);
// (errors[Object.keys(errors)[0]]?.ref as HTMLInputElement).focus();
console.log("field state", getFieldState("name"));
console.log("error object", getFieldState("name").error);
console.log(errors);
}
});
}
} >
Submit
</a>
Works for react-hook-form version 7+
import "./styles.css";
import classNames from "classnames";
import { useForm } from "react-hook-form";
export default function App() {
const {
register,
handleSubmit,
trigger,
watch,
formState: { errors }
} = useForm();
const onSubmit = (data: any) => {
// send request
};
return (
<div className="App">
<div className="form">
<form onSubmit={handleSubmit(onSubmit)}>
// ...
</form>
</div>
// Not under the form
<button
className="button"
onClick={async () => {
// Check the validation of field
const validationResult = await trigger('name', {shouldFocus: true});
// Show the result of validation
console.log('Field name is valid? ->', validationResult);
console.log('Field name current value', getValues().name);
// Submit or not submit the form
if (validationResult) { handleSubmit(onSubmit)(); }
}}
>
Submit
</button>
</div>
);
}
Related
How can I create an order of adding items. I should be able to add one after the other item during on click. By default it should display the Textarea first and then blockquote ( see below )
a) When a user click on Text area button, it should add one after the blockquote.
b) Then when the user clicks on Code area button, it should add after Textarea. Could someone please advise ?
CSB link: https://codesandbox.io/s/distracted-worker-26jztf?file=/src/App.js
Something similar >> Expected behaviour: https://jsfiddle.net/nve8qdbu/8/
import "./styles.css";
import React, { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { useForm } from "react-hook-form";
const blogListData = [
{
id: 1,
heading: "React state",
date: "22-May-2022",
tag: "React",
count: "3"
},
{
id: 2,
heading: "Cypress testing detailss",
date: "22-May-2022",
tag: "Cypress",
count: "5"
}
];
const Admin = () => {
const [createImageTag, setImageTag] = useState("");
const [fields, setFields] = useState([{ value: null }]);
const [createCode, setCreateCode] = useState([{ value: null }]);
const [blogList, setBlogList] = useState([]);
const {
register,
handleSubmit,
formState: { errors },
reset
} = useForm();
useEffect(() => {
setBlogList(blogListData);
}, []);
function handleChangeTextArea(i, event) {
const values = [...fields];
values[i].value = event.target.value;
setFields(values);
}
function handleChangeCode(i, event) {
const codeValues = [...createCode];
codeValues[i].value = event.currentTarget.innerText;
setCreateCode(codeValues);
}
function handleTextAreaAdd() {
const values = [...fields];
values.push({ value: null });
setFields(values);
}
function handleCodeAreaAdd() {
const codeValues = [...createCode];
codeValues.push({ value: null });
setCreateCode(codeValues);
}
function handleImageAreaAdd() {
const image = [...createImageTag];
image.push({ value: null });
setCreateCode(image);
}
function handleRemoveText(i) {
const values = [...fields];
values.splice(i, 1);
setFields(values);
}
function handleRemoveCode(i) {
const codeValues = [...createCode];
codeValues.splice(i, 1);
setCreateCode(codeValues);
}
const handleLogout = () => {
localStorage.removeItem("loginEmail");
};
return (
<div id="App">
<div className="parent">
<div className="adminSection">
<h1>Create a new blog</h1>
<div className="row">
<div className="logout">
<img
src="/images/logout.png"
alt="Logout"
onClick={handleLogout}
></img>
</div>
<div className="createBlogSection">
<div className="row">
<button
onClick={() => handleTextAreaAdd()}
className="textAreaBtn"
>
Text Area
</button>
<button
onClick={() => handleCodeAreaAdd()}
className="codeAreaBtn"
>
Code Area
</button>
<button
onClick={() => handleImageAreaAdd()}
className="imageAreaBtn"
>
Add Image
</button>
</div>{" "}
<br></br>
<div className="row">
{fields.map((field, idx) => {
return (
<div key={`${field}-${idx}`} className="dtextArea">
<button
type="button"
onClick={() => handleRemoveText(idx)}
className="closeElement"
>
X
</button>
<textarea
type="text"
id="blogtext"
placeholder="Enter your text here"
className="defaultTextArea"
{...register("blogtext", {
required: true,
minLength: {
value: 25,
message: "Minimum length of 25 letters"
}
})}
value={field.value || ""}
onChange={(e) => handleChangeTextArea(idx, e)}
/>
<span className="validationmsg">
{errors.blogtext &&
errors.blogtext.type === "required" && (
<span>Blog text is required !</span>
)}
{errors.blogtext && (
<span>{errors.blogtext.message}</span>
)}
</span>
</div>
);
})}
</div>
<div className="row">
{createCode.map((code, idx) => {
return (
<div key={`${code}-${idx}`} className="dCodeArea">
<button
type="button"
onClick={() => handleRemoveCode(idx)}
className="closeElement"
>
X
</button>
<blockquote
type="text"
id="blogCode"
contentEditable="true"
className="codehighlight"
placeholder="Enter your code here"
{...register("blogCode", {
required: true
})}
value={code.value || ""}
onInput={(e) => handleChangeCode(idx, e)}
/>
</div>
);
})}
</div>
<div className="row">
</div>
<div className="row">
<div className="submitSection">
<input type="submit" className="submitBtn" />
</div>
</div>
</div>
</div>
</div>
<div className="blogListSection">
<h1>Edit blogs</h1>
<div className="row">
<div className="editBlogSection">
{blogList.map(({ id, heading, count }) => (
<a
key={id}
href="https://www.testingsite.com/"
className="blogitems"
>
<pre>
<span>{count}</span> {heading}
</pre>
</a>
))}
</div>
</div>
</div>
</div>
</div>
);
};
export default Admin;
react is designed for components . each of your list elements should be refactored by a component.then it would be easier. i think a single react component could do the trick
I'm looking to implement the form validation using react-hook. However, I'm facing some trouble in doing so as I've also added some stuff on my own under the handleSubmit and I'm not really sure how to go about it.
export default function Contact() {
const [message, setMessage] = useState(false);
const [alert, setAlert] = useState(true);
const { register, errors} = useForm();
const [showElement, setShowElement] = React.useState(false);
const handleSubmit = (e) => {
e.preventDefault();
emailjs.sendForm('', '', e.target, '')
.then((result) => {
console.log(result.text);
}, (error) => {
console.log(error.text);
});
e.target.reset();
setMessage(true);
setShowElement(true);
setTimeout(function () {
setShowElement(false);
}, 4000);
};
const onSubmit= data=>{
console.log(data);
}
return (
<div className="right">
<h2>Contact Me</h2>
<form onSubmit={handleSubmit} id="contactform">
<input type="text" placeholder="Name" name="name" ref={register({required: true, minLength: 2})}
required />
<button type="submit">Send</button>
</form>
{showElement ? (
<div className="submitmsg">
{message && (
<span> Messaged received. I'll respond to your query ASAP! </span>
)}
</div>
) : (
<div> </div>
)}{" "}
</div>
)
}
Thank you!
React hook form provides the handeSubmit method that receives the form data after validations. Also, you must use the errors object to show errors in the UI.
Here is the sandbox link: https://codesandbox.io/s/exciting-dust-df5ft?file=/src/App.js
I have updated your code accordingly:
import { useState } from "react";
import React from "react";
import emailjs from "emailjs-com";
import { useForm } from "react-hook-form";
export default function Contact() {
const [message, setMessage] = useState(false);
const {
register,
handleSubmit,
formState: { errors }
} = useForm();
const [showElement, setShowElement] = React.useState(false);
const onSubmit = (data) => {
emailjs
.send(
"service_t1ccrgq",
"template_gmmcyzr",
data,
"user_d0vUwhmqvbIYhEsyZF8tu"
)
.then(
(result) => {
console.log(result.text);
},
(error) => {
console.log(error.text);
}
);
setMessage(true);
setShowElement(true);
setTimeout(function () {
setShowElement(false);
}, 4000);
};
return (
<div className="contact" id="contact">
<div className="left">
<img className="contactme" src="asset/email.gif" />
</div>
<div className="right">
<h2>Contact Me</h2>
<form onSubmit={handleSubmit(onSubmit)} id="contactform">
<input
type="text"
placeholder="Name"
name="name"
{...register("name", {
required: "Name is Required",
minLength: {
value: 3,
message: "Should be greater than 3 characters"
}
})}
/>
<input
type="tel"
placeholder="Mobile Number"
name="mobile"
{...register("mobile", {
required: "Mobile Number is Required",
minLength: {
value: 3,
message: "Should be greater than 3 characters"
}
})}
/>
<input
type="text"
placeholder="Email"
name="email"
{...register("email", {
required: "Email is Required",
minLength: {
value: 3,
message: "Should be greater than 3 characters"
}
})}
/>
<textarea
placeholder="Message"
required
name="message"
{...register("message", {
required: "Message is Required",
minLength: {
value: 3,
message: "Should be greater than 3 characters"
}
})}
></textarea>
<button type="submit">Send</button>
</form>
{showElement ? (
<div className="submitmsg">
{message && (
<span> Messaged received. I'll respond to your query ASAP! </span>
)}
</div>
) : (
<div> </div>
)}{" "}
</div>
{errors.name && (
<div>
<span>{errors.name.message}</span>
</div>
)}
{errors.message && (
<div>
<span>{errors.message.message}</span>
</div>
)}
{errors.email && (
<div>
<span>{errors.email.message}</span>
</div>
)}
{errors.mobile && (
<div>
<span>{errors.mobile.message}</span>
</div>
)}
</div>
);
}
You have to first initialize handleSubmit as below.
const {handleSubmit} = useForm();
Then in the form, onSubmit should be as below.
<form onSubmit={handleSubmit(onSubmit)}>
"onSubmit" is the method that is used to write the code in submitting form.
Regards.
In your code, it should be as below.
const onSubmit = (e) => {
e.preventDefault();
emailjs.sendForm('', '', e.target, '')
.then((result) => {
console.log(result.text);
}, (error) => {
console.log(error.text);
});
e.target.reset();
setMessage(true);
setShowElement(true);
setTimeout(function () {
setShowElement(false);
}, 4000);
};
The Error Message:
If i dont use the Inputs inside div then it works perfectly but when i use Input inside div it shows me this error.
I wanted to keep the hook related stuff separated so it look clean.
why does it only works when its not inside a div?
Login.tsx
import { useHistory } from "react-router-dom";
import { useForm } from "react-hook-form";
import useAuth from "./../hooks/useAuth";
import { Form, Input } from "../components/FormGroup";
import MacNav from "../components/MacNav";
import { loginActionUrl } from "./../services/ApiLinks";
import {
fetchPostResopnse,
successPopUp,
errorPopUp,
} from "./../services/FormHelper";
type Tinputs = {
username: string;
password: string;
};
function Login() {
const auth = useAuth();
const history = useHistory();
const methods = useForm<Tinputs>();
const onSubmit = async (data: Tinputs) => {
const result = await fetchPostResopnse(loginActionUrl, data);
if (result.isAuth) {
successPopUp("Credentials Matched", () => {
auth.signIn(result);
history.push("/admin/dashboard");
});
} else {
errorPopUp("Credentials Does Not Matched");
}
};
return (
<div>
<MacNav />
<div className="section-secondary">
<div className="container">
<div className="contact-form-wrapper">
<div className="title-lg text-center">Enter Your Credentials</div>
<Form formMethods={methods} handler={onSubmit} submitBtn="Submit">
{/*If i dont use Input inside div it works*/}
<div>
<Input name="username" rule={{ required: true }} />
</div>
<Input name="password" rule={{ required: true }} />
</Form>
</div>
</div>
</div>
</div>
);
}
export default Login;
I have wrote the form components here.
FormGroup.tsx
import React from "react";
const Form = ({ children, formMethods, handler, submitBtn }: any) => {
return (
<form onSubmit={formMethods.handleSubmit(handler)}>
{React.Children.map(children, (child) => {
return child.props.name ? (
<div>
{React.createElement(child.type, {
...{
...child.props,
register: formMethods.register,
key: child.props.name,
},
})}
{child.props?.rule && formMethods.errors[child.props.name] && (
<div className="text-danger">
*
{formMethods.errors[child.props.name].message
? formMethods.errors[child.props.name].message
: `${child.props.name} is required`}
</div>
)}
</div>
) : (
child
);
})}
{submitBtn && <button type="submit">{submitBtn}</button>}
</form>
);
};
const Input = ({ register, name, label, rule, ...rest }: any) => {
label = label ? label : name?.charAt(0).toUpperCase() + name?.slice(1);
return (
<div>
<label htmlFor={name}>{label}</label>
<input name={name} ref={register(rule)} {...rest} />
</div>
);
};
const Textarea = ({ register, name, label, rule, ...rest }: any) => {
label = label ? label : name?.charAt(0).toUpperCase() + name?.slice(1);
return (
<div>
<label htmlFor={name}>{label}</label>
<textarea name={name} ref={register(rule)} {...rest}></textarea>
</div>
);
};
const SubmitButton = ({ name, ...rest }: any) => {
return (
<button type="submit" {...rest}>
{name}
</button>
);
};
export { Form, Input, Textarea, SubmitButton };
[1]: https://i.stack.imgur.com/PvEUA.png
Hello according to your code, what happened it's expected
the div doesn't have name so according to this code
{React.Children.map(children, (child) => {
return child.props.name ? (
<div>
{React.createElement(child.type, {
...{
...child.props,
register: formMethods.register,
key: child.props.name,
},
})}
{child.props?.rule && formMethods.errors[child.props.name] && (
<div className="text-danger">
*
{formMethods.errors[child.props.name].message
? formMethods.errors[child.props.name].message
: `${child.props.name} is required`}
</div>
)}
</div>
) : (
child
);
})}
And the below child
<div>
<Input name="username" rule={{ required: true }} />
/div>
The Input component will be rendrered without register prop, so when it will try to call it here, however it's value is undefined, what will cause an error
ref={register(rule)}
I suggest to create a new component
const InputWithDiv = (props) => (
<div>
<Input rule={{ required: true }} {..props} />
/div>
);
and use it like below
<Form formMethods={methods} handler={onSubmit} submitBtn="Submit">
<InputWithDiv name="username" />
<Input name="password" rule={{ required: true }} />
</Form>
I am new to React and can't seem to get my Materialize Picker to work at all.
I have all the Materialize installed and imported.
It displays correctly and opens correctly but when I select a date, I get an error displaying every time and can't figure out why.
TypeError: Cannot read property 'completeBy' of undefined
I have added all my code below for my test page where it's currently sitting.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { addToDo } from '../../store/actions/todoActions';
import { Redirect } from 'react-router-dom';
import M from "materialize-css";
//import moment from 'moment';
class AddToDo extends Component {
state = {
title: '',
content: '',
assignTo: '',
completeBy: new Date(),
format: 'ddd d, mmm',
//formatMoment: 'ddd D, MMM'
}
handleChange = (e) => {
this.setState({
[e.target.id]: e.target.value
})
}
handleSubmit = (e) => {
e.preventDefault();
this.props.addToDo(this.state);
this.props.history.push('/');
}
handleCancel = (e) => {
e.preventDefault();
this.props.history.push('/');
}
canBeSubmitted() {
const { title, content, assignTo } = this.state;
return title.length > 0 && content.length > 0 && assignTo.length > 0;
}
componentDidMount() {
let selects = document.querySelectorAll('select');
let elems = document.querySelectorAll('.datepicker');
M.Datepicker.init(elems, {
defaultDate: new Date(),
format: this.state.format,
container: 'body',
onSelect: function(date) {
this.setState({ completeBy: this.state.completeBy }); // Errors here
},
autoClose: true
});
M.FormSelect.init(selects, {});
}
render() {
const { auth } = this.props;
const isEnabled = this.canBeSubmitted();
if (!auth.uid) {
return <Redirect to='/login' />
}
return (
<div className="container">
<form className="white" onSubmit={ this.handleSubmit }>
<h5 className="grey-text text-darken-3">Add a new todo item</h5>
<div className="input-field">
<input type="text" id='title' onChange={ this.handleChange } autoFocus />
<label htmlFor="title">Todo title <span className="red-text">*</span></label>
</div>
<div className="input-field">
<textarea id="content" className="materialize-textarea" onChange={ this.handleChange }></textarea>
<label htmlFor="content">Todo content <span className="red-text">*</span></label>
</div>
<div className="input-field">
<select id="assignTo" onChange={ this.handleChange }>
<option value="default" disabled selected>Please select</option>
<option value="Kyle">Kyle</option>
<option value="Mike">Mike</option>
<option value="Tony">Tony</option>
</select>
<label htmlFor="assignTo">Assign todo to <span className="red-text">*</span></label>
</div>
<div className="input-field">
<label htmlFor="completeBy">To be completed by</label>
<input
id="completeBy"
type="text"
className="datepicker dateset"
// defaultValue={ moment(this.state.completeBy).format(
// this.state.formatMoment
// )}
/>
</div>
<div className="row">
<div className="col s12 l1">
<button className="btn pink lighten-1 col s12" disabled={!isEnabled}>Add</button>
</div>
<div className="col s12 l1">
<button onClick={this.handleCancel} className="btn yellow darken-2 col s12">Cancel</button>
</div>
</div>
</form>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
auth: state.firebase.auth
}
}
const mapDispatchToProps = (dispatch) => {
return {
addToDo: (todo) => dispatch(addToDo(todo))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(AddToDo)
Some may recognise the code base as I was following Net Ninja tutorials but adding to it for my learning. I have also had a look at the following stack question and tried their solution as its the same code as mine also but it does not work for me.
I have checked my package.json and I am using "materialize-css": "^1.0.0-rc.2" and I am not using react-materialize.
Screenshots
Initial load
Click in the date field
Set a date, picker closes and get
Most of the time I use arrow functions when I define callbacks. Because it handles the scope of this differently than a function. Try to replace onSelect callback to an arrow function:
M.Datepicker.init(elems, {
defaultDate: new Date(),
format: this.state.format,
container: 'body',
onSelect: (date) => {
this.setState({ completeBy: this.state.completeBy });
},
autoClose: true
});
I would like to conditionally display errors in my form.
The way formik works is that if you change one field all validations are ran and all errors returned even thought you changed just one.
I would like to display the error only if the field was TOUCHED and I would like a field to be TOUCHED onChange. The first change to the field should make it touched.
At the moment formik is touching fields just on submit. How would I be able to touch it onChange?
This is my current form:
const optionsForSelect = (collection) => {
return collection.map(item => ({
value: item.id,
label: item.name
}))
}
const validationSchema = yup.object().shape({
length: yup
.number()
.min(1, 'Length should be a positive non-zero integer')
.required(),
frame_rate: yup
.string()
.required()
})
class SpecificationsForm extends React.PureComponent {
render() {
const {
values,
handleChange,
handleInputChange,
handleSelectChange,
handleBlur,
errors,
touched
} = this.props;
const debouncedHandleChange = debounce(handleChange, 200)
console.log(errors)
console.log('TOUCHED')
console.log(touched)
return (
<div className="panel panel-default specifications-panel" id="js-turbosquid-product-specifications-panel">
<div className="panel-heading">
<a href="#" className="js-more-info" data-toggle="collapse" data-target="#specifications-panel-instructions" tabIndex="-1">
Specifications
<i className="fa fa-question-circle" />
</a>
</div>
<div className="panel-body panel-collapse collapse in" id="specification-panel-body">
<div className="panel-body-container">
<div id="specifications-panel-instructions" className="panel-instructions collapse" />
<div className="row">
<div className="col-xs-6">
<PanelInputField
label='Length'
value={ values.length }
onChange={ (e) => handleInputChange(e, debouncedHandleChange) }
formName='turbosquid_product_form_length'
fieldName='length'
/>
<div className="form-field-error">{errors.length ? errors.length : "No Error"}</div>
<PanelSelectField
label='Frame Rate'
value={ values.frame_rate }
onChange={ ({value}) => handleSelectChange('frame_rate', value) }
formName='turbosquid_product_form_frame_rate'
fieldName='frame_rate'
options={ optionsForSelect(frameRateDropdownData) }
searchable={ false }
clearable={ false }
/>
</div>
<div className="col-xs-6">
<PanelCheckBox
label='Biped'
checked={ values.biped }
onChange={ (e) => handleInputChange(e, debouncedHandleChange) }
fieldName='biped'
formName='turbosquid_product_form_biped'
/>
<PanelCheckBox
label='Loopable'
checked={ values.loopable }
onChange={ (e) => handleInputChange(e, debouncedHandleChange) }
fieldName='loopable'
formName='turbosquid_product_form_loopable'
/>
</div>
</div>
</div>
</div>
</div>
)
}
}
const ProductSpecificationsMotionCapturePanel = withFormik({
validationSchema,
enableReinitialize: true,
mapPropsToValues: (props) => (props),
handleInputChange: (props) => (props.handleInputChange),
handleSelectChange: (props) => (props.handleSelectChange),
})(SpecificationsForm)
export default ProductSpecificationsMotionCapturePanel
To touch a Formik field onChange, you can do this:
<Formik
initialValues={initialValues}
onSubmit={(values) => {
//submit form
}}>
{({ setFieldTouched, handleChange }) => {
return (
<Form>
<Field
name="type"
onChange={e => {
setFieldTouched('type');
handleChange(e);
}} />
</Form>
)
}}
Hi I think it's not doable onChange but you can do so when the input is blurred and you need to use the handleBlur function: onBlur={handleBlur}.
Also errors being an object you can display it only when a given [input name] has one.
Take a look at when validations are ran here in the docs: https://jaredpalmer.com/formik/docs/guides/validation#when-does-validation-run
A workaround would be to use formik's method getFieldMeta and pass your field's name and call the value prop which isn't null when you type something.
errorMessage={
formikProps.getFieldMeta("username").value
? formikProps.errors.username
: ""
}
It's possible to set the touched value without invoking validation again and one can do so by using the useFormik hook available in React 18+.
import { useFormik } from "formik";
const Component = () => {
const { setFieldTouched, handleChanged } = useFormik({
validateOnChange: true,
validateOnBlur: true,
validateOnMount: true,
});
const handleInput = (e) => {
setFieldTouched(e.target.name, true, false);
handleChanged && handleChanged(e);
};
return <input name="email" onInput={handleInput} />;
};