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')".

enter image description here

Edit React Dynamic Form

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;
2
  • 1
    You have to create multiple refs, see here stackoverflow.com/questions/61245376/… Commented Jan 11, 2022 at 12:16
  • Thanks, i didn't know about multiple refs. I tried but i didn't solve it. I will try more. Commented Jan 11, 2022 at 16:44

1 Answer 1

4

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)}
 />
Sign up to request clarification or add additional context in comments.

8 Comments

Well, in reality i have a big form with many static inputs and two dynamic input fields. I only send a CodeSandBox with a part of my form where the problem appears . Will it work in a big form with many static inputs and two dynamic input fields? I mean if it is good practice putting the ref in the form tag etc.
Yes, it would work for a form having thousands of inputs enclosed. Don't worry. You can verify that yourself. :)
Also, as mentioned in the following doc, don't overuse refs. That's actually not a good practice. Just have a single ref on form and that will solve your problem. reactjs.org/docs/refs-and-the-dom.html#dont-overuse-refs
I tried it but in my case it doesn't work as i want. Because i have overused refs. i have a different ref for each input.
Don't use ref for each input. Just have one ref on the form tag and that will solve your problem. Check the code I mentioned above.
|

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.