I have a React form with dynamic input fields that a user can add and remove input fields. All inputs are required. If all input fields are filled and the form is submitted then i console.log() an array with the values, else empty input gets focused. The problem is that if i whitespace(backspace) inside input fields, the form can be submitted and i don't want this. How can i fix this? I tried to use the trim() method (const registriesIsValid = registryValues.every((registry)=>registry.name.trim() !== "" && registry.code.trim() !== "");
) but im getting "typeError Cannot read properties of undefined (reading 'trim')".
Below my code without trim method.
App.js
import React, { useState, useRef } from "react";
import Wrapper from "./Wrapper";
import "./styles.css";
export default function App() {
const [registryValues, setRegistryValues] = useState([]);
const [registryValuesTouched, setRegistryValuesTouched] = useState(false);
const fieldRef = useRef();
// const registriesRef = useRef();
const registriesIsValid = registryValues.every(
(registry) => registry.name !== "" && registry.code !== ""
);
const registriesInputIsInvalid = !registriesIsValid;
let handleRegistryChange = (i, event) => {
setRegistryValuesTouched(true);
let newRegistryValues = [...registryValues];
if (event.target.name === "name") {
newRegistryValues[i].registry["name"] = event.target.value;
}
if (event.target.name === "code") {
newRegistryValues[i].code = event.target.value;
}
// newRegistryValues[i][event.target.name] = event.target.value;
setRegistryValues(newRegistryValues);
};
let addRegistryFields = (event) => {
event.preventDefault();
setRegistryValues([
...registryValues,
{ registry: { name: "" }, code: "" }
]);
};
let removeRegistryFields = (i, event) => {
event.preventDefault();
let newRegistryValues = [...registryValues];
newRegistryValues.splice(i, 1);
setRegistryValues(newRegistryValues);
};
const submitHandler = (event) => {
event.preventDefault();
setRegistryValuesTouched(true);
for (let elem of fieldRef.current.elements) {
if (elem.dataset.required && !elem.value) {
elem.focus();
return;
}
}
console.log(registryValues);
setRegistryValuesTouched(false);
};
return (
<Wrapper>
<form onSubmit={submitHandler} ref={fieldRef}>
<fieldset>
<legend>
<h3 className="govgr-heading-m margin-top">Registries</h3>
</legend>
<div
className={`${
registriesInputIsInvalid ? "govgr-form-group__error" : ""
}`}
>
{registriesInputIsInvalid && (
<p className="govgr-error-message">
<span className="govgr-visually-hidden">Λάθος:</span>All fields
are required.
</p>
)}
{registriesInputIsInvalid && (
<p className="govgr-error-message">
<span className="govgr-visually-hidden">Λάθος:</span>You must
fill in registry Name and registry Code.
</p>
)}
{registryValues.map((element, index) => (
<div key={index} className="flex-row registry-margin-bottom">
<div className="registry-flex-basis">
<div className="govgr-form-group">
<label
className="govgr-label govgr-!-font-weight-bold"
htmlFor="name"
>
Registry Name*
</label>
<input
className={`govgr-input govgr-!-width-three-quarter ${
element.registry["name"].trim() === ""
? "govgr-error-input"
: ""
}`}
type="text"
name="name"
data-required="true"
value={element.registry["name"] || ""}
onChange={(e) => handleRegistryChange(index, e)}
/>
</div>
<div className="govgr-form-group">
<label
className="govgr-label govgr-!-font-weight-bold"
htmlFor="code"
>
Registry Code*
</label>
<input
className={`govgr-input govgr-!-width-three-quarter ${
element.code.trim() === "" ? "govgr-error-input" : ""
}`}
type="text"
name="code"
data-required="true"
value={element.code || ""}
onChange={(e) => handleRegistryChange(index, e)}
/>
</div>
</div>
<button
className="govgr-btn govgr-btn-warning remove-registry"
onClick={(e) => removeRegistryFields(index, e)}
>
X
</button>
</div>
))}
</div>
</fieldset>
<button
className="govgr-btn govgr-btn-secondary button-registry"
onClick={addRegistryFields}
>
Add Registry
</button>
<button
className="govgr-btn govgr-btn-primary btn-center"
type="submit"
>
Save
</button>
</form>
</Wrapper>
);
}
I fixed it. Actually, inside my submitHandler i forgot to check (if !registriesIsValid){...}
.
Also, my registry object has different structure. So i change validation to:
const registriesIsValid = registryValues.every((registry)=>registry.registry["name"].trim() !== "" && registry.code.trim() !== "");
App.js
import React, { useState, useRef } from "react";
import Wrapper from "./Wrapper";
import "./styles.css";
export default function App() {
const [registryValues, setRegistryValues] = useState([]);
const [registryValuesTouched, setRegistryValuesTouched] = useState(false);
const fieldRef = useRef();
// const registriesRef = useRef();
//1st change
const registriesIsValid = registryValues.every(
(registry) => registry.registry["name"].trim() !== "" && registry.code.trim() !== ""
);
const registriesInputIsInvalid = !registriesIsValid;
let handleRegistryChange = (i, event) => {
setRegistryValuesTouched(true);
let newRegistryValues = [...registryValues];
if (event.target.name === "name") {
newRegistryValues[i].registry["name"] = event.target.value;
}
if (event.target.name === "code") {
newRegistryValues[i].code = event.target.value;
}
// newRegistryValues[i][event.target.name] = event.target.value;
setRegistryValues(newRegistryValues);
};
let addRegistryFields = (event) => {
event.preventDefault();
setRegistryValues([
...registryValues,
{ registry: { name: "" }, code: "" }
]);
};
let removeRegistryFields = (i, event) => {
event.preventDefault();
let newRegistryValues = [...registryValues];
newRegistryValues.splice(i, 1);
setRegistryValues(newRegistryValues);
};
const submitHandler = (event) => {
event.preventDefault();
setRegistryValuesTouched(true);
//2nd change
if (!registriesIsValid) {
for (let elem of fieldRef.current.elements) {
if (elem.dataset.required && !elem.value.trim()) {
elem.focus();
return;
}
}
return;
}
console.log(registryValues);
setRegistryValuesTouched(false);
};
return (
<Wrapper>
<form onSubmit={submitHandler} ref={fieldRef}>
<fieldset>
<legend>
<h3 className="govgr-heading-m margin-top">Registries</h3>
</legend>
<div
className={`${
registriesInputIsInvalid ? "govgr-form-group__error" : ""
}`}
>
{registriesInputIsInvalid && (
<p className="govgr-error-message">
<span className="govgr-visually-hidden">Λάθος:</span>All fields
are required.
</p>
)}
{registriesInputIsInvalid && (
<p className="govgr-error-message">
<span className="govgr-visually-hidden">Λάθος:</span>You must
fill in registry Name and registry Code.
</p>
)}
{registryValues.map((element, index) => (
<div key={index} className="flex-row registry-margin-bottom">
<div className="registry-flex-basis">
<div className="govgr-form-group">
<label
className="govgr-label govgr-!-font-weight-bold"
htmlFor="name"
>
Registry Name*
</label>
<input
className={`govgr-input govgr-!-width-three-quarter ${
element.registry["name"].trim() === ""
? "govgr-error-input"
: ""
}`}
type="text"
name="name"
data-required="true"
value={element.registry["name"] || ""}
onChange={(e) => handleRegistryChange(index, e)}
/>
</div>
<div className="govgr-form-group">
<label
className="govgr-label govgr-!-font-weight-bold"
htmlFor="code"
>
Registry Code*
</label>
<input
className={`govgr-input govgr-!-width-three-quarter ${
element.code.trim() === "" ? "govgr-error-input" : ""
}`}
type="text"
name="code"
data-required="true"
value={element.code || ""}
onChange={(e) => handleRegistryChange(index, e)}
/>
</div>
</div>
<button
className="govgr-btn govgr-btn-warning remove-registry"
onClick={(e) => removeRegistryFields(index, e)}
>
X
</button>
</div>
))}
</div>
</fieldset>
<button
className="govgr-btn govgr-btn-secondary button-registry"
onClick={addRegistryFields}
>
Add Registry
</button>
<button
className="govgr-btn govgr-btn-primary btn-center"
type="submit"
>
Save
</button>
</form>
</Wrapper>
);
}