Home > Software design >  onChange on a dynamic element to collect objects in an array
onChange on a dynamic element to collect objects in an array

Time:11-15

I have inputs which values I collect and send to firebase(as strings, objects and arrays).

all inputs work fine. the only one I'm struggling with is a dynamic one, which is rendered through a map() method on an array of inputs. I use the onChange method on the input and am able to get to unsatisfactory results: either getting only the last value from the the array of inputs, or getting every charecter entery as an event thus creating an array like this: ['b', 'be', 't', 'th', 'the', 'g', 'gy', 'g', 'gu', 'guy'].

what is the correct method? tried using both array.push() and useState([]).

so I'm trying to collect the values of dynamically rendered inputs. here are the relevant code pieces:

const [newTodo, setNewTodo] = useState([]);

const [todoInput, setTodoInput] = useState([{ todo: "" }]);

this returns the values (be, the, guy) as a newTodo = ['b', 'be', 't', 'th', 'the', 'g', 'gy', 'g', 'gu', 'guy']

 {todoInput.map((singleTodo, index) => (
        <div key={index}>
          <input
            type="text"
            className="todo-input"
            onChange={(event) => {
              newTodo.push(event.target.value);
            }}

this return newTodo as ['guy']:

{todoInput.map((singleTodo, index) => (
        <div key={index}>
          <input
            type="text"
            className="todo-input"
            onChange={(event) => {
              setNewTodo([event.target.value]);
            }}

this also returns newTodo = ['b', 'be', 't', 'th', 'the', 'g', 'gy', 'g', 'gu', 'guy']:

{todoInput.map((singleTodo, index) => (
        <div key={index}>
          <input
            type="text"
            className="todo-input"
            onChange={(event) => {
              setNewTodo([...newTodo, event.target.value]);
            }}

the entire component for context:

const AddTask = () => {
  const db = getFirestore();
  const colTaskRef = collection(db, "Task");
  const [newTitle, setNewTitle] = useState("");
  const [newInCharge, setNewInCharge] = useState("");
  const [newCollabs, setNewCollabs] = useState([]);
  const [newPriority, setNewPriority] = useState("");
  const [newTodo, setNewTodo] = useState([]);

  const [todoInput, setTodoInput] = useState([{ todo: "" }]);
  const handleTodoAdd = () => {
    setTodoInput([...todoInput, { todo: "" }]);
  };
  const handleTodoRemove = (index) => {
    const list = [...todoInput];
    list.splice(index, 1);
    setTodoInput(list);
  };

  const handleSelect = (e) => {
    setNewInCharge(e.target.value);
  };
  const handleCheck = (e) => {
    setNewCollabs([...newCollabs, e.target.value]);
  };

  const handleClick = () => {
    console.log(newTodo);
    createTask();
  };

  const createTask = async () => {
    await addDoc(colTaskRef, {
      Title: newTitle,
      InCharge: newInCharge,
      Priority: newPriority,
      Todos: newTodo,
      Collaborators: newCollabs,
      InProgress: false,
      Completed: false,
    });
  };
  return (
    <div className="container add__task-container">
      <input
        className="title-input"
        type="text"
        placeholder="Task Title"
        onChange={(event) => {
          setNewTitle(event.target.value);
        }}
      />
      <label htmlFor="selectInCharge"> Who's in charge of this task?</label>
      {<GetCollaborators handleSelect={handleSelect} />}
      <p className="collaborators__checkbox-title">
        Who do you want to collaborate with?
      </p>
      {<GetCollaboratorsCheckBox handleCheck={handleCheck} />}

      <label className="priority-label" htmlFor="priority">
        Priority?
      </label>
      <select
        name="priority"
        className="priority-selector"
        onChange={(event) => {
          setNewPriority(event.target.value);
        }}
      >
        <option value="top"> Top </option>
        <option value="first">First</option>
        <option value="second">Second</option>
        <option value="last">Bottom</option>
      </select>
      <p className="todos__add-title">To Do list:</p>
      {todoInput.map((singleTodo, index) => (
        <div key={index}>
          <input
            type="text"
            className="todo-input"
            onChange={(event) => {
              setNewTodo([...newTodo, event.target.value]);
            }}
          />
          {todoInput.length - 1 === index && todoInput.length < 5 && (
            <button className="plus-btn" onClick={handleTodoAdd}>
               
            </button>
          )}
          {todoInput.length > 1 && (
            <button
              className="minus-btn"
              onClick={() => handleTodoRemove(index)}
            >
              -
            </button>
          )}
        </div>
      ))}

      <button className="btn btn__task-submit" onClick={handleClick}>
        Submit Task
      </button>
    </div>
  );
};

CodePudding user response:

if you don't want the eventlistener to trigger on change, don't use the onChange event.

If you tell your function to execute every time the input changes, it will do exactly that. You should probably use some other form of event in order to trigger your listener - maybe a button to press once the input is done. Or on a certain buttonpress (spacebar / enter) or onBlur. I don't know at which point you want the input to actually be added to the array. Depending on that, choose an event, that actually fits your purpouse (onClick for a buttonpress for example)

Reacts onChange uses the html/JS event oninput and not onchange

  • Related