I have a React form with dynamic input fields that a user can add and remove input fields. I validate when i submit the form. If input is empty, input gets focused with useRef hook. The problem is that if i have two inputs empty, so i add a second input and remove it after, i am getting typeError "Cannot read properties of null (reading 'focus')".
App.js
import React, { useState, useRef } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
const [fields, setFields] = useState([""]);
const fieldRef = useRef();
const fieldsIsValid =
fields.length >= 1 && fields.every((field) => field.trim() !== "");
function handleChange(i, event) {
const values = [...fields];
values[i] = event.target.value;
setFields(values);
}
function handleAdd() {
const values = [...fields];
values.push("");
setFields(values);
}
function handleRemove(i) {
const values = [...fields];
values.splice(i, 1);
setFields(values);
}
function submitHandler(event) {
event.preventDefault();
if (!fieldsIsValid) {
if (fields.length >= 1) {
fieldRef.current.focus();
return;
}
return;
}
console.log(fields);
}
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<form onSubmit={submitHandler}>
<button type="button" onClick={() => handleAdd()}>
Add Input
</button>
{!fieldsIsValid && <p className="error">Input is required</p>}
{fields.map((field, idx) => {
return (
<div key={`${"input"}-${idx}`}>
<input
type="text"
placeholder="Enter text"
value={field || ""}
ref={fieldRef}
onChange={(e) => handleChange(idx, e)}
/>
<button type="button" onClick={() => handleRemove(idx)}>
X
</button>
</div>
);
})}
<button className="margin-top" type="submit">
Submit
</button>
</form>
</div>
);
}
export default App;
CodePudding user response:
As Andreas already mentioned, you need to create multiple refs for multiple inputs. There is always one-to-one mapping/assignment of React refs to DOM nodes. If you use the same ref at multiple positions, the ref would be linked to the last node you have used it for. To help you understand this, check the error you are getting here. It says, "cannot read properties of null" i.e. the value of fieldRef.current is null since the DOM node fieldRef was last mapped with (the last input field) does not exist after deletion in the DOM tree.
You can try to implement the same functionality by instead putting the ref on the form tag and not the input tags, like so:
<form onSubmit={submitHandler} ref={fieldRef}>
function submitHandler(event) {
event.preventDefault();
for (let elem of fieldRef.current.elements) {
if (elem.type === 'text' && elem.dataset.required && !elem.value) {
elem.focus()
return
}
}
}
<input
type="text"
data-required="true" // <---- Add this here
placeholder="Enter text"
value={field || ""}
onChange={(e) => handleChange(idx, e)}
/>

