Tuesday, 12 October 2021

Nested dynamic form creation - State shared for dynamic added fields

I´m trying to create a nested dynamic form using react that is able to generate dynamic input fields based on already generated dynamic input fields.

What am I trying to do?

User should be able to create a playlist that consists of

  • Playlistname (1:1/ only once per form)
  • YoutubeURL (As much as the user wants can be added
  • StartTime/ EndTime (As much as the user wants can be added to EACH youtubeURL)

This is the starting form

Playlist-Start

I´m already able to add new Start/End-Time fields to a youtubeURL

enter image description here

But if I afterwards click on "add new fields" and generate a new input for youtubeURL, the generated Start/End time already share the state of the previous entered Start/end from the old youtubeURL.

I don't get where I´m doing this wrong.

enter image description here

And also overall the form doesn't feel like beeing really stable right now... If someone has an idea how I can improve this form and also fix my shared state issue, would be great.

Here is my code:

import React, { useState } from "react";
import Form from "react-bootstrap/Form";
import Button from "react-bootstrap/Button";

const PlaylistForm = () => {
  const [formPlaylistName, setFormPlaylistName] = useState("");
  const [formPlaylistYoutubeUrl, setFormPlaylistYoutubeUrl] = useState([
    { youtubeUrl: "" },
  ]);
  const [formPlaylistYoutubeUrlPlayTimes, setFormPlaylistYoutubeUrlPlayTimes] =
    useState([{ start: 0, end: 0 }]);

  // start formPlaylistYoutubeUrl
  let handleChangePlaylistYoutubeUrl = (i: any, e: any) => {
    let newFormValues = [...formPlaylistYoutubeUrl];
    // @ts-expect-error: Let's ignore a compile error like this unreachable code
    newFormValues[i][e.target.name] = e.target.value;
    setFormPlaylistYoutubeUrl(newFormValues);
  };

  let addFormFieldsPlaylistYoutubeUrl = () => {
    setFormPlaylistYoutubeUrl([...formPlaylistYoutubeUrl, { youtubeUrl: "" }]);
  };

  let removeFormFieldsPlaylistYoutubeUrl = (i: any) => {
    let newFormValues = [...formPlaylistYoutubeUrl];
    newFormValues.splice(i, 1);
    setFormPlaylistYoutubeUrl(newFormValues);
  };

  // end formPlaylistYoutubeUrl

  //start formPlaylistYoutubeUrlPlayTimes
  let handleChangePlaylistYoutubeUrlPlayTimes = (i: any, e: any) => {
    let newFormValues = [...formPlaylistYoutubeUrlPlayTimes];
    console.log(e.target.value);
    // @ts-expect-error: Let's ignore a compile error like this unreachable code
    newFormValues[i][e.target.name] = e.target.value;
    setFormPlaylistYoutubeUrlPlayTimes(newFormValues);
  };

  let addFormFieldsPlaylistYoutubeUrlPlayTimes = () => {
    setFormPlaylistYoutubeUrlPlayTimes([
      ...formPlaylistYoutubeUrlPlayTimes,
      { start: 0, end: 0 },
    ]);
  };

  let removeFormFieldsPlaylistYoutubeUrlPlayTimes = (i: any) => {
    let newFormValues = [...formPlaylistYoutubeUrlPlayTimes];
    newFormValues.splice(i, 1);
    setFormPlaylistYoutubeUrlPlayTimes(newFormValues);
  };

  //end formPlaylistYoutubeUrlPlayTimes

  let handleSubmit = (event: any) => {
    event.preventDefault();
    alert(formPlaylistName);
    alert(JSON.stringify(formPlaylistYoutubeUrl));
    alert(JSON.stringify(formPlaylistYoutubeUrlPlayTimes));
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>Playlist name</label>
      <Form.Control
        type="text"
        name="playlistName"
        value={formPlaylistName}
        onChange={(e) => setFormPlaylistName(e.target.value)}
      />
      {formPlaylistYoutubeUrl.map((element, index) => (
        <div className="form-inline" key={index}>
          <label>Name</label>
          <Form.Control
            type="text"
            name="youtubeUrl"
            value={element.youtubeUrl || ""}
            onChange={(e) => handleChangePlaylistYoutubeUrl(index, e)}
          />

          {index ? (
            <button
              type="button"
              className="button remove"
              onClick={() => removeFormFieldsPlaylistYoutubeUrl(index)}
            >
              Remove
            </button>
          ) : null}

          {formPlaylistYoutubeUrlPlayTimes.map((element1, index1) => (
            <div className="form-inline" key={index1}>
              <label>Start time</label>
              <Form.Control
                type="text"
                name="start"
                value={element1.start || ""}
                onChange={(e) =>
                  handleChangePlaylistYoutubeUrlPlayTimes(index1, e)
                }
              />
              <label>End time</label>
              <Form.Control
                type="text"
                name="end"
                value={element1.end || ""}
                onChange={(e) =>
                  handleChangePlaylistYoutubeUrlPlayTimes(index1, e)
                }
              />
              {index1 ? (
                <div>
                  <button
                    type="button"
                    className="button remove"
                    onClick={() =>
                      removeFormFieldsPlaylistYoutubeUrlPlayTimes(index1)
                    }
                  >
                    Remove
                  </button>
                </div>
              ) : null}
              <button
                type="button"
                className="button add"
                onClick={() => addFormFieldsPlaylistYoutubeUrlPlayTimes()}
              >
                Add time
              </button>
            </div>
          ))}
        </div>
      ))}

      <div className="button-section">
        <Button
          variant="primary"
          type="button"
          onClick={() => addFormFieldsPlaylistYoutubeUrl()}
        >
          Add new Field
        </Button>
        <Button variant="primary" type="submit">
          Create new shorturl
        </Button>
      </div>
    </form>
  );
};

export default PlaylistForm;


from Nested dynamic form creation - State shared for dynamic added fields

No comments:

Post a Comment