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')". I tried with dynamic useRef input too but i can't fix it.
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;
You will need to have the inputRef
to always track which one do you want to focus. eg after delete, point the inputRef to the last non-empty input.
an alternative idea (better I think) of doing this is via regular js code. in this way you don't need to worry about keep tracking the last, you just focus the first input that is empty.
const inputRefs = document.querySelectorAll("#myform input");
for (let i = 0; i < inputRefs.length; i++) {
if (!inputRefs[i].value && inputRefs[i].required) {
inputRefs[i].focus();
break;
}
}
https://codesandbox.io/s/react-dynamic-form-forked-8gnww
Edit: updated to generate some required inputs