Home > Software design >  React dynamic form input fields typeError undefined trim()
React dynamic form input fields typeError undefined trim()

Time:01-24

I have a React form with dynamic input fields that a user can add and remove input fields. All inputs are required. If all input fields are filled and the form is submitted then i console.log() an array with the values, else empty input gets focused. The problem is that if i whitespace(backspace) inside input fields, the form can be submitted and i don't want this. How can i fix this? I tried to use the trim() method (const registriesIsValid = registryValues.every((registry)=>registry.name.trim() !== "" && registry.code.trim() !== "");) but im getting "typeError Cannot read properties of undefined (reading 'trim')".

typeError

Below my code without trim method.

Edit react dynamic form input fields

App.js

import React, { useState, useRef } from "react";
import Wrapper from "./Wrapper";
import "./styles.css";

export default function App() {
  const [registryValues, setRegistryValues] = useState([]);
  const [registryValuesTouched, setRegistryValuesTouched] = useState(false);
  const fieldRef = useRef();
  // const registriesRef = useRef();

  const registriesIsValid = registryValues.every(
    (registry) => registry.name !== "" && registry.code !== ""
  );
  const registriesInputIsInvalid = !registriesIsValid;

  let handleRegistryChange = (i, event) => {
    setRegistryValuesTouched(true);
    let newRegistryValues = [...registryValues];
    if (event.target.name === "name") {
      newRegistryValues[i].registry["name"] = event.target.value;
    }
    if (event.target.name === "code") {
      newRegistryValues[i].code = event.target.value;
    }
    // newRegistryValues[i][event.target.name] = event.target.value;
    setRegistryValues(newRegistryValues);
  };

  let addRegistryFields = (event) => {
    event.preventDefault();
    setRegistryValues([
      ...registryValues,
      { registry: { name: "" }, code: "" }
    ]);
  };

  let removeRegistryFields = (i, event) => {
    event.preventDefault();
    let newRegistryValues = [...registryValues];
    newRegistryValues.splice(i, 1);
    setRegistryValues(newRegistryValues);
  };

  const submitHandler = (event) => {
    event.preventDefault();
    setRegistryValuesTouched(true);

    for (let elem of fieldRef.current.elements) {
      if (elem.dataset.required && !elem.value) {
        elem.focus();
        return;
      }
    }

    console.log(registryValues);

    setRegistryValuesTouched(false);
  };

  return (
    <Wrapper>
      <form onSubmit={submitHandler} ref={fieldRef}>
        <fieldset>
          <legend>
            <h3 className="govgr-heading-m margin-top">Registries</h3>
          </legend>
          <div
            className={`${
              registriesInputIsInvalid ? "govgr-form-group__error" : ""
            }`}
          >
            {registriesInputIsInvalid && (
              <p className="govgr-error-message">
                <span className="govgr-visually-hidden">Λάθος:</span>All fields
                are required.
              </p>
            )}
            {registriesInputIsInvalid && (
              <p className="govgr-error-message">
                <span className="govgr-visually-hidden">Λάθος:</span>You must
                fill in registry Name and registry Code.
              </p>
            )}
            {registryValues.map((element, index) => (
              <div key={index} className="flex-row registry-margin-bottom">
                <div className="registry-flex-basis">
                  <div className="govgr-form-group">
                    <label
                      className="govgr-label govgr-!-font-weight-bold"
                      htmlFor="name"
                    >
                      Registry Name*
                    </label>
                    <input
                      className={`govgr-input govgr-!-width-three-quarter ${
                        element.registry["name"].trim() === ""
                          ? "govgr-error-input"
                          : ""
                      }`}
                      type="text"
                      name="name"
                      data-required="true"
                      value={element.registry["name"] || ""}
                      onChange={(e) => handleRegistryChange(index, e)}
                    />
                  </div>

                  <div className="govgr-form-group">
                    <label
                      className="govgr-label govgr-!-font-weight-bold"
                      htmlFor="code"
                    >
                      Registry Code*
                    </label>
                    <input
                      className={`govgr-input govgr-!-width-three-quarter ${
                        element.code.trim() === "" ? "govgr-error-input" : ""
                      }`}
                      type="text"
                      name="code"
                      data-required="true"
                      value={element.code || ""}
                      onChange={(e) => handleRegistryChange(index, e)}
                    />
                  </div>
                </div>
                <button
                  className="govgr-btn govgr-btn-warning remove-registry"
                  onClick={(e) => removeRegistryFields(index, e)}
                >
                  X
                </button>
              </div>
            ))}
          </div>
        </fieldset>
        <button
          className="govgr-btn govgr-btn-secondary button-registry"
          onClick={addRegistryFields}
        >
          Add Registry
        </button>

        <button
          className="govgr-btn govgr-btn-primary btn-center"
          type="submit"
        >
          Save
        </button>
      </form>
    </Wrapper>
  );
}

CodePudding user response:

Seems pretty straightforward with simple check:

if (registry.name && registry.name.trim() !== "")
... 

Right hand of the && will only be evaluated if the left hand returns true.
This differs from || where left and right hand will always both be evaluated.

CodePudding user response:

I fixed it. Actually, inside my submitHandler i forgot to check (if !registriesIsValid){...} .

Also, my registry object has different structure. So i change validation to:

const registriesIsValid = registryValues.every((registry)=>registry.registry["name"].trim() !== "" && registry.code.trim() !== "");

App.js

import React, { useState, useRef } from "react";
import Wrapper from "./Wrapper";
import "./styles.css";

export default function App() {
  const [registryValues, setRegistryValues] = useState([]);
  const [registryValuesTouched, setRegistryValuesTouched] = useState(false);
  const fieldRef = useRef();
  // const registriesRef = useRef();
   
  //1st change
  const registriesIsValid = registryValues.every(
    (registry) => registry.registry["name"].trim() !== "" && registry.code.trim() !== ""
  );
  const registriesInputIsInvalid = !registriesIsValid;

  let handleRegistryChange = (i, event) => {
    setRegistryValuesTouched(true);
    let newRegistryValues = [...registryValues];
    if (event.target.name === "name") {
      newRegistryValues[i].registry["name"] = event.target.value;
    }
    if (event.target.name === "code") {
      newRegistryValues[i].code = event.target.value;
    }
    // newRegistryValues[i][event.target.name] = event.target.value;
    setRegistryValues(newRegistryValues);
  };

  let addRegistryFields = (event) => {
    event.preventDefault();
    setRegistryValues([
      ...registryValues,
      { registry: { name: "" }, code: "" }
    ]);
  };

  let removeRegistryFields = (i, event) => {
    event.preventDefault();
    let newRegistryValues = [...registryValues];
    newRegistryValues.splice(i, 1);
    setRegistryValues(newRegistryValues);
  };

  const submitHandler = (event) => {
    event.preventDefault();
    setRegistryValuesTouched(true);


    //2nd change
    if (!registriesIsValid) {
      for (let elem of fieldRef.current.elements) {
        if (elem.dataset.required && !elem.value.trim()) {
          elem.focus();
          return;
        }
      }
      return;
    }
    
    console.log(registryValues);

    setRegistryValuesTouched(false);
  };

  return (
    <Wrapper>
      <form onSubmit={submitHandler} ref={fieldRef}>
        <fieldset>
          <legend>
            <h3 className="govgr-heading-m margin-top">Registries</h3>
          </legend>
          <div
            className={`${
              registriesInputIsInvalid ? "govgr-form-group__error" : ""
            }`}
          >
            {registriesInputIsInvalid && (
              <p className="govgr-error-message">
                <span className="govgr-visually-hidden">Λάθος:</span>All fields
                are required.
              </p>
            )}
            {registriesInputIsInvalid && (
              <p className="govgr-error-message">
                <span className="govgr-visually-hidden">Λάθος:</span>You must
                fill in registry Name and registry Code.
              </p>
            )}
            {registryValues.map((element, index) => (
              <div key={index} className="flex-row registry-margin-bottom">
                <div className="registry-flex-basis">
                  <div className="govgr-form-group">
                    <label
                      className="govgr-label govgr-!-font-weight-bold"
                      htmlFor="name"
                    >
                      Registry Name*
                    </label>
                    <input
                      className={`govgr-input govgr-!-width-three-quarter ${
                        element.registry["name"].trim() === ""
                          ? "govgr-error-input"
                          : ""
                      }`}
                      type="text"
                      name="name"
                      data-required="true"
                      value={element.registry["name"] || ""}
                      onChange={(e) => handleRegistryChange(index, e)}
                    />
                  </div>

                  <div className="govgr-form-group">
                    <label
                      className="govgr-label govgr-!-font-weight-bold"
                      htmlFor="code"
                    >
                      Registry Code*
                    </label>
                    <input
                      className={`govgr-input govgr-!-width-three-quarter ${
                        element.code.trim() === "" ? "govgr-error-input" : ""
                      }`}
                      type="text"
                      name="code"
                      data-required="true"
                      value={element.code || ""}
                      onChange={(e) => handleRegistryChange(index, e)}
                    />
                  </div>
                </div>
                <button
                  className="govgr-btn govgr-btn-warning remove-registry"
                  onClick={(e) => removeRegistryFields(index, e)}
                >
                  X
                </button>
              </div>
            ))}
          </div>
        </fieldset>
        <button
          className="govgr-btn govgr-btn-secondary button-registry"
          onClick={addRegistryFields}
        >
          Add Registry
        </button>

        <button
          className="govgr-btn govgr-btn-primary btn-center"
          type="submit"
        >
          Save
        </button>
      </form>
    </Wrapper>
  );
}
  •  Tags:  
  • Related