reactjsinputarray.prototype.mapinput-field

React - map n times group of input fields and take their value into array of objects


I am trying to map one react element for n times. Element consist two input fields: x and y.

I have managed to do this, but when I enter some value in x field, all x fields on all Element are getting that value.

Second part of problem is when I try to take values of input fields into array of objects (eg. [{x:10, y:20},{x:30, y:70},{x:100, y:5}]), there is error that "Map is not a function."

Here is code and CodePen link: https://codesandbox.io/s/vigilant-shape-d250yz?file=/src/App.js

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

function App() {
  const inputRef = useRef(null);

  const [fieldsCount, setFieldsCount] = useState(0);

  const [xPolygonInput, setXInput] = useState(0);
  const [yPolygonInput, setYInput] = useState(0);

  let secondForm = document.getElementsByClassName("second-form");
  console.log(secondForm);

  let items = [];

  let createArrayOfObjects = () => {
    console.log(xPolygonInput, yPolygonInput);
    items.push(
      secondForm.map(() => {
        return {
          x: { xPolygonInput },
          y: { yPolygonInput }
        };
      })
    );
    console.log(items);
  };

  return (
    <div className="app">
      <h1>Check is point inide of polygon or not</h1>
      <div className="start-form">
        <p>Select number from 3 to 10 for number of Polygon angles</p>
        <input
          type="number"
          id="nr-of-angles"
          name="nr-ofangles"
          min="3"
          max="10"
          required
          ref={inputRef}
        ></input>
        <button
          className="btn"
          onClick={() => setFieldsCount(inputRef.current.value)}
        >
          Submit
        </button>
      </div>
      <div className="coordinates">
        <div className="coordinates-wrap">
          <div className="angle-wrapper">
            <p>Angle Coordinates</p>
            <div className="angle-inner">
              {Array(parseInt(fieldsCount))
                .fill()
                .map((i, index) => (
                  <SecondForm
                    id={index}
                    key={index}
                    xPolygonInput={xPolygonInput}
                    yPolygonInput={yPolygonInput}
                    setXInput={setXInput}
                    setYInput={setYInput}
                    className="second-form"
                  >
                    <p>Group {index + 1}</p>
                  </SecondForm>
                ))}
            </div>
            <br />
            <button onClick={createArrayOfObjects} className="btn">
              Create Array of Objects
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}

export default App;

Replacing react element with simple html code did not helped. First part of problem (repeating input on all x or y fields) may have some connection with .fill(), but if I remove it map does not work... Any help is welcomed! :)


Solution

  • I would suggest storing the entire positions (x, y) as an array of objects.

    Callback would then update the correct position based on index (or ID if applicable).

    You can take a look at the example code below.

    SecondForm.jsx

    function SecondForm({ x, y, onXChange, onYChange }) {
      // const [input, setInput] = useState(props?.value ?? '');
    
      return (
        <div className="angle-input">
          <form>
            {/* <label>Enter X and Y coordinates for Polygon angles</label> */}
            <div className="angle-coordinates">
              <p>Angle</p>
              <input
                type="number"
                min="0"
                max="500"
                placeholder="x"
                className="xPolygon"
                name="nr-ofangles"
                value={x}
                onChange={onXChange}
                required
              ></input>
              <input
                type="number"
                min="0"
                max="400"
                placeholder="y"
                className="yPolygon"
                name="nr-ofangles"
                value={y}
                onChange={onYChange}
                required
              ></input>
            </div>
          </form>
        </div>
      );
    }
    
    export default SecondForm;
    

    App.jsx

    import "./styles.css";
    import { useRef, useState } from "react";
    import SecondForm from "./SecondForm";
    
    function App() {
      const inputRef = useRef(null);
    
      const [positions, setPositions] = useState([]);
    
      const handleCreatePositions = () => {
        const pos = Array.from(
          { length: parseInt(inputRef.current.value, 10) },
          () => ({ x: 0, y: 0 })
        );
        setPositions(pos);
      };
    
      const handleOnInputChange = (e, index, axis) => {
        setPositions((prevPositions) => {
          const newPositions = [...prevPositions];
          newPositions[index] = {
            ...newPositions[index],
            [axis]: e.target.value
          };
          return newPositions;
        });
      };
    
      return (
        <div className="app">
          <h1>Check is point inide of polygon or not</h1>
          <div className="start-form">
            <p>Select number from 3 to 10 for number of Polygon angles</p>
            <input
              type="number"
              id="nr-of-angles"
              name="nr-ofangles"
              min="3"
              max="10"
              required
              ref={inputRef}
            ></input>
            <button className="btn" onClick={handleCreatePositions}>
              Submit
            </button>
          </div>
          <div className="coordinates">
            <div className="coordinates-wrap">
              <div className="angle-wrapper">
                <p>Angle Coordinates</p>
                <div className="angle-inner">
                  {positions.map((position, index) => (
                    <SecondForm
                      id={index}
                      key={index}
                      x={position.x}
                      y={position.y}
                      onXChange={(e) => handleOnInputChange(e, index, "x")}
                      onYChange={(e) => handleOnInputChange(e, index, "y")}
                      className="second-form"
                    >
                      <p>Group {index + 1}</p>
                    </SecondForm>
                  ))}
                </div>
                <br />
                <button onClick={() => console.log(positions)} className="btn">
                  Create Array of Objects
                </button>
              </div>
            </div>
          </div>
        </div>
      );
    }
    
    export default App;
    

    Edit 74428251