Handling User Interaction
A common challenge in form validation is "noise control." You don't want to scream errors at a user before they've even touched a field.
Traditionally, libraries use an isDirty flag to track if a user has modified a field. Since Vest is UI-agnostic (it doesn't touch your DOM or listen to events), it doesn't track "dirty" state for you.
Instead, Vest provides two powerful tools to handle user interaction: isTested() and suite.focus().
1. isTested(): The Vest Alternative to isDirty​
When you want to decide if you should show an error message, you usually want to know: "Has this field actually been validated yet?"
If a field hasn't been validated, it usually means the user hasn't interacted with it. Vest tracks this for you.
const result = suite.get();
// Only show errors if the field has actually been tested
const shouldShowError =
result.hasErrors('username') && result.isTested('username');
if (shouldShowError) {
renderError(result.getErrors('username'));
}
This pattern ensures that empty, untouched fields don't show "Required" errors when the form first loads.
2. Validating on Interaction with suite.focus()​
When a user blurs a field or types, you often want to validate only that specific field, while keeping the rest of the form state intact.
Vest 6 introduces suite.focus(). This tells Vest to run validations for specific fields, while skipping others.
// On Blur handler
function handleBlur(fieldName, formData) {
// 1. Tell Vest to focus ONLY on the blurred field
// 2. Run the suite with the current data
suite.focus({ only: fieldName }).run(formData);
}
Why use focus()?​
- Performance: It skips expensive tests (like async checks) for fields the user isn't touching.
- User Experience: It updates the state for the current field without accidentally flagging other fields as "tested" or "invalid" before the user reaches them.
Combine suite.focus() with isTested() for the best UX:
- Use
focus({ only: fieldName })in youronBlurhandler to validate only the current field - Use
isTested(fieldName)when rendering to decide whether to show errors
Complete Example​
import suite from './validation';
function Form() {
const [formData, setFormData] = useState({});
const [result, setResult] = useState(suite.get());
const handleChange = e => {
const { name, value } = e.target;
setFormData(prev => ({ ...prev, [name]: value }));
};
const handleBlur = e => {
const { name } = e.target;
// Validate only the blurred field
const res = suite.focus({ only: name }).run(formData);
setResult(res);
};
const handleSubmit = e => {
e.preventDefault();
// Validate all fields on submit
const res = suite.run(formData);
setResult(res);
if (res.isValid()) {
// Submit the form
}
};
// Only show error if field was tested
const showError = fieldName => {
return result.isTested(fieldName) && result.hasErrors(fieldName);
};
return (
<form onSubmit={handleSubmit}>
<input name="username" onChange={handleChange} onBlur={handleBlur} />
{showError('username') && <span>{result.getError('username')}</span>}
<button type="submit">Submit</button>
</form>
);
}
Summary​
| Goal | Traditional Approach | Vest Approach |
|---|---|---|
| Did the user touch this? | Check field.isDirty | Check result.isTested('field') |
| Validate on Blur | Call validateField('field') | Call suite.focus({ only: 'field' }).run(data) |
By combining isTested() (to hide premature errors) and suite.focus() (to update specific fields), you get precise control over the user experience without tightly coupling your validation to the DOM.
Related​
- Focused Updates - Deep dive into
suite.focus() - Accessing the Result - Learn about
isTested()and other result methods