I have a input component that does validation. In a reduced form it looks like this:
const InputField = ({validation, name}) => {
const [value, setValue] = useState("");
const [error, setError] = useState(false);
const handleChange = (e) => {
let errors = 0;
if (validation.includes("required") && val === "") {
errors++;
}
if (errors > 0) {
setError(true);
} else {
setError(false);
}
setValue(e.target.value)
}
return (
<input
type="text"
value={value}
onChange={handleChange}
name={name}
/>
);
};
If I am using this component multiple times in a parent like
const Parnet = () => {
// ๐ฅ๐ฅ๐ฅ help required here
const groupedErrorValueBool = true;
return (
<>
<InputField name="Name" validation="required" />
<InputField name="Email" validation="required" />
<InputField name="Birthday" validation="required" />
<button disabled={groupedErrorValueBool}>Submit</button>
</>
);
How can I get a grouped value of errors? Like: I want to disable a submit button, if any field has any error.
Ideally I know which component has an error, so that I can print meaningful comments.
The groupedErrorValueBool state would need to be in your parent component then, then you give the state handler to the children
const InputField = ({ validation, setGroupErrorBool }) => {
const [value, setValue] = useState("");
const [error, setError] = useState(false);
const handleChange = (e) => {
let errors = 0;
if (validation.includes("required") && val === "") {
errors++;
}
if (errors > 0) {
setError(true);
setGroupErrorBool(true);
} else {
setError(false);
}
setValue(e.target.value);
};
return <input type="text" value={value} onChange={handleChange} />;
};
const Parent = () => {
const [groupErrorBool, setGroupErrorBool] = useState(false);
return (
<>
<InputField validation="required" setGroupErrorBool={setGroupErrorBool} />
<InputField validation="required" setGroupErrorBool={setGroupErrorBool} />
<button disabled={groupErrorBool}>Submit</button>
</>
);
};
Form stuff is admittedly can be very difficult. That's why I use "Formik" for my React forms. Formik I believe will use a parent/overarching form control element with rendered props technique to expose form state handlers, which could also be something you can try yourself
You could do it like this:
const InputField = ({validation, onErrorChanged}) => {
const [value, setValue] = React.useState("test");
const [error, setError] = React.useState(false);
const handleChange = (e) => {
let errors = 0;
if (validation.includes("required") && e.target.value === "") {
errors++;
}
if (errors > 0 && !error) {
// we have error(s) and the error state is set to false
setError(true);
onErrorChanged(true);
} else if (errors === 0 && error){
// we don't have any error but the error state is set to true
setError(false);
onErrorChanged(false);
}
setValue(e.target.value)
}
return (
<input
type="text"
value={value}
onChange={handleChange}
/>
);
};
const InputGroup = () => {
const [errors, setErrors] = React.useState(0);
function errorChanged(hasError){
if(hasError) setErrors(errors + 1);
else setErrors(errors - 1);
}
return (
<React.Fragment>
<InputField validation="required" onErrorChanged={errorChanged}/>
<InputField validation="required" onErrorChanged={errorChanged}/>
<InputField validation="required" onErrorChanged={errorChanged}/>
<button disabled={errors === 0 ? false: true}>Submit</button>
</React.Fragment>
);
}
ReactDOM.render(<InputGroup/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
You need to pass a callback function to the children that is called whenever the error state changes on a child element i. e.
error state is currently set to falseerror state is currently set to trueIn the parent component we need a state errors which is a counter that counts the number of child component that have errors. Additionally we need to define the above mentioned callback function such that it increments the counter whenever a child component reports it has an error and decrements the counter when a child component reports it has switched to state no error.
If the counter equals zero we know we don't have any error in any of our child components, if it's not null we know how many child components have errors.