Home > Software design >  React dynamic form input useRef typeError
React dynamic form input useRef typeError

Time:01-15

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;

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)}
 />
  •  Tags:  
  • Related