Amr Khaled
Amr Khaled

Reputation: 457

How to update the state based on my initial state

What I'm trying to do here is to build a dynamic component, that will be responsible to take an array of objects and the form will be built based on my formState. So I made my initial State and used .map to make the loop over the state and mapped the keys to making the label, value, and inputs appear based on the state. but my problem is at onChange. How to update the value key in every object and set the new state for it. any advice, please.

import { useState } from "react";
import InputText from "./components";
import useForm from "./hooks/useForm";

function App() {
  interface formStateT {
    id: number;
    label: string;
    value: any;
    error: string;
  }

  const formState = [
    {
      id: 0,
      label: "firstName",
      value: "",
      error: "",
    },
    {
      id: 1,
      label: "lastName",
      value: "",
      error: "",
    },
  ];

  const { form, validate, setForm, checkValidHandler } = useForm(formState);
  const [error, setError] = useState("");

  const submitFormHandler = (e: { preventDefault: () => void }) => {
    e.preventDefault();

    checkValidHandler();

    // write form logic
    // setError() will be used to take the error message

    console.log(form);
  };

  return (
    <form onSubmit={(e) => submitFormHandler(e)}>
      {form.map((f: formStateT) => (
        <InputText
          key={f.id}
          label={f.label}
          value={f.value}
          onChange={(e) => {
            // Im trying here to update the value key of every label key.
            // setForm({ ...form, [f.label.valueOf()]: f.value })
          }}
          valid={f.value === "" ? validate.notValid : validate.valid}
          errorMsg={error === "" ? f.error : error}
          classes={"class"}
        />
      ))}
      <button>Submit</button>
    </form>
  );
}

export default App;

Upvotes: 1

Views: 237

Answers (1)

Drew Reese
Drew Reese

Reputation: 202608

From your comment, f.value = e.target.value; is a state mutation and should be avoided, the setForm([...form]); is masking the mutation.

In App create an onChangeHandler function that takes the onChange event object and the index you want to update. Unpack the value from the onChange event and update the state. The handler should use a functional state update to update from the previous state, and create a shallow copy of the form array, using the index to shallow copy and update the correct array element.

Example:

// curried function to close over index in scope, and
// return handler function to consume event object
const onChangeHandler = index => e => {
  const { value } = e.target;
  setForm(form => form.map((el, i) => 
    i === index
      ? { ...el, value }
      : el
  ));
};

...

<form onSubmit={submitFormHandler}>
  {form.map((f: formStateT, index: number) => (
    <InputText
      key={f.id}
      label={f.label}
      value={f.value}
      onChange={onChangeHandler(index)} // <-- invoke and pass mapped index
      valid={f.value === "" ? validate.notValid : validate.valid}
      errorMsg={error === "" ? f.error : error}
      classes={"class"}
    />
  ))}
  <button>Submit</button>
</form>

Upvotes: 1

Related Questions