Form validation often triggers unnecessary re-renders, especially in large React apps. In this guide, we’ll build a no-re-render validation system using React Context, Refs, and a touch of functional magic — perfect for complex forms where performance matters.
Why Avoid Re-Renders?
Each validation change (like user typing) can cause entire form sections to re-render, creating lag. By using refs and a pub-sub model via context, we keep validation outside of the render lifecycle entirely.
Step 1: Create a Validation Context
This will manage subscribers (inputs) without triggering renders.
// ValidationContext.js
import { createContext, useContext, useRef } from "react";
const ValidationContext = createContext();
export function ValidationProvider({ children }) {
const fields = useRef(new Map());
const register = (name, validateFn) => {
fields.current.set(name, validateFn);
};
const validateAll = () => {
let valid = true;
fields.current.forEach((validateFn) => {
if (!validateFn()) valid = false;
});
return valid;
};
return (
{children}
);
}
export const useValidation = () => useContext(ValidationContext);
Step 2: Register Inputs Without State
Each input registers its validator but doesn’t subscribe to global form state.
// ValidatedInput.js
import { useEffect, useRef } from "react";
import { useValidation } from "./ValidationContext";
export function ValidatedInput({ name, validate, ...props }) {
const inputRef = useRef();
const { register } = useValidation();
useEffect(() => {
register(name, () => validate(inputRef.current.value));
}, [name, validate, register]);
return ;
}
Step 3: Validate the Form on Submit
The form can validate all fields at once without state updates.
// Form.js
import { ValidationProvider, useValidation } from "./ValidationContext";
import { ValidatedInput } from "./ValidatedInput";
function Form() {
const { validateAll } = useValidation();
const handleSubmit = (e) => {
e.preventDefault();
if (validateAll()) {
alert("Form is valid!");
} else {
alert("Form has errors.");
}
};
return (
);
}
export default function App() {
return (
);
}
How It Works
Instead of React state managing field errors, each input registers its validator with a central manager (context + ref map). The form then calls all validators at submit without re-rendering inputs during typing, leading to major performance wins for large forms.
Pros and Cons
✅ Pros
- No render overhead for validation updates
- Ultra-fast even with 100+ fields
- Fully decoupled and testable validators
⚠️ Cons
- Manual registration adds slight complexity
- Not ideal for reactive, instant-feedback UIs without extra hooks
- Browser-native validation still needs to be handled separately
🚀 Alternatives
- react-hook-form: Extremely efficient form library with no re-render philosophy
- Formik + FastField: Scoped optimizations in traditional form libraries
- Zod validation: Use schema-based parsing instead of ad-hoc functions
Summary
If your forms feel sluggish or complicated, think outside the React state machine. Using refs and a simple validation context unlocks huge speed gains and scales beautifully for enterprise apps.
For a much more extensive guide on getting the most out of React portals, check out my full 24-page PDF file on Gumroad. It's available for just $10:
Using React Portals Like a Pro.
If this was useful, you can support me here: buymeacoffee.com/hexshift