2

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.

enter image description here

CodeSandBox

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;

1 Answer 1

1

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

Sign up to request clarification or add additional context in comments.

3 Comments

Actually i have a big form with static text inputs,a email input, a phone input,a mapped select and some dynamic input fields like this etc.. Each of those have different validation.I only post a part of my form. For example, some static text inputs aren't required so they don't need to get focus. So i don't think so that this will work. I think i need dynamic refs but i can't fix it.
you can always add additional fields like required or data-required or even just a className or something to those inputs that you need to get focus, I don't think you need dynamic ref. inputRefs[i] are all the refs within the form. you can update the query to fit your needs
It works with your answer, so i am accepting as the answer. But, i done it with putting ref on the form tag.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.