Jacky
Jacky

Reputation: 49

ReactJS form need to submit 2 times to work

  const [name, setName] = useState("");
  const [age, setAge] = useState("");
  const initialValues = {
    name: "",
    age: "",
  };
  const [formValues, setFormValues] = useState(initialValues);
  const [formErrors, setFormErrors] = useState({});
  const [isSubmit, setIsSubmit] = useState(false);

  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormValues({ ...formValues, [name]: value });
  };

  const handleSubmit = (e) => {
    setFormErrors(validate(formValues));
    setIsSubmit(true);
  };

 const validate = (values) => {
    const errors = {};
    if (!values.name) {
      errors.name = "Name is required";
    }
    if (!values.age) {
      errors.age= "Age is required";
    }
    return errors;
  };

 const userCreate = async () => {
    await api.post("/createuser", {
      name,
      age,
    });
  };

 return (
    <div class="container">
      <Form
        onSubmit={
          Object.keys(formErrors).length === 0 && isSubmit
            ? userCreate
            : handleSubmit
        }
      >
        <Form.Field>
          <label>Name</label>
          <input
            name="name"
            onChange={(e) => {
              setName(e.target.value);
              handleChange(e);
            }}
            values={formValues.name}
          />
          <span className="error-message">{formErrors.name}</span>
        </Form.Field>
        <Form.Field>
          <label>Age</label>
          <input
            name="age"
            onChange={(e) => {
              setAge(e.target.value);
              handleChange(e);
            }}
            values={formValues.age}
          />
          <p className="error-message">{formErrors.age}</p>
        </Form.Field>
        <Button type="submit">Submit</Button>
      </Form>
    </div>
  );

I'm trying to use axios to do POST method for creating user.

I got everything works fine but there's one small problem but I don't know how to fix.

The problem is that I always need to submit the form 2 times to make the POST request. There's nothing happen in the first submit, but it will work in the second submit.

Does anyone know what's wrong with my code?

Edited

According to @DBS solution. I'm trying to follow the steps but now the form can't submit anymore. Can someone let me know if I missed something?

const [name, setName] = useState("");
  const [age, setAge] = useState("");
  const initialValues = {
    name: "",
    age: "",
  };
  const [formValues, setFormValues] = useState(initialValues);
  const [formErrors, setFormErrors] = useState({});
  const [isSubmitting, setIsSubmitting] = useState(false);

  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormValues({ ...formValues, [name]: value });
  };




const handleSubmit = (e) => {
    if (!Object.keys(formErrors).length && !isSubmitting) {
      setFormErrors(validate(formValues));
    } else {
      userCreate();
      setisSubmitting(true);
    }
  };

 const validate = (values) => {
    const errors = {};
    if (!values.name) {
      errors.name = "Name is required";
    }
    if (!values.age) {
      errors.age= "Age is required";
    }
    return errors;
  };

 const userCreate = async () => {
    await api.post("/createuser", {
      name,
      age,
    });
  };

 return (
    <div class="container">
      <Form
        onSubmit={handleSubmit}
      >
        <Form.Field>
          <label>Name</label>
          <input
            name="name"
            onChange={(e) => {
              setName(e.target.value);
              handleChange(e);
            }}
            values={formValues.name}
          />
          <span className="error-message">{formErrors.name}</span>
        </Form.Field>
        <Form.Field>
          <label>Age</label>
          <input
            name="age"
            onChange={(e) => {
              setAge(e.target.value);
              handleChange(e);
            }}
            values={formValues.age}
          />
          <p className="error-message">{formErrors.age}</p>
        <

Upvotes: 0

Views: 44

Answers (2)

Helphin
Helphin

Reputation: 183

Are you using the isSubmitting flag for something? if not below might be work for you.

If there is no error, calling the create method

const handleSubmit = (e) => {
  setFormErrors(validate(formValues));

  if(Object.keys(formErrors).length === 0) {
    userCreate();
  }
};

if isSubmitting is used to check the submit or create in progress

const handleSubmit = (e) => {
  setFormErrors(validate(formValues));
    
  if(Object.keys(formErrors).length === 0) {
    setIsSubmitting(true);
    userCreate();
  }
};

The flag isSubmitting should be set as false on API is success or failed setIsSubmitting(false)

Upvotes: 0

DBS
DBS

Reputation: 9959

The issue here is your isSubmit, it is required to be true for userCreate to be called:

onSubmit={
  Object.keys(formErrors).length === 0 && isSubmit
    ? userCreate
    : handleSubmit
}

But it starts as false:

const [isSubmit, setIsSubmit] = useState(false);

And is only ever updated when handleSubmit is called (which, confusingly, is only called when the validation fails)

So your current code does this:

  • isSubmit is false
  • Submit is clicked, handleSubmit is called and isSubmit is set to true
  • Submit is clicked again, now isSubmit is true it will call userCreate

To solve this, there are a few different approaches, but I would:

  • Move all the submit handler logic into onSubmit={handleSubmit} (To keep things clear)
  • Inside there, do your error length check (0 error) and isSubmit (Which I would probably rename to isSubmitting for clarity, and make sure it's false) (E.g. !Object.keys(formErrors).length && !isSubmitting)
  • If there are errors, show the appropriate message (Leaving isSubmitting as false)
  • If not, call userCreate (And set isSubmitting to true)
  • Lastly, if this can be submitted multiple times, add an effect/callback/then to reset isSubmitting once the call is complete.

Upvotes: 2

Related Questions