arda ersoy
arda ersoy

Reputation: 449

react-hook-form onSubmit not triggered

import React, { useState } from "react";
import FileBase64 from "react-file-base64";
import { useDispatch } from "react-redux";
import { makeStyles } from "@material-ui/core/styles";
import { TextField, Select, Input, MenuItem, Button } from "@material-ui/core";
import { useForm, Controller } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";
import { updatePost } from "../actions/post";

const useStyles = makeStyles((theme) => ({
  textField: {
    marginBottom: theme.spacing(2),
  },
  buttons: {
    marginTop: theme.spacing(2),
  },
}));

const tags = ["fun", "programming", "health", "science"];

const postSchema = yup.object().shape({
  title: yup.string().required(),
  subtitle: yup.string().required(),
  content: yup.string().min(20).required(),
  tag: yup.mixed().oneOf(tags),
});

const EditPostForm = ({ history, post, closeEditMode }) => {
  const dispatch = useDispatch();

  const [file, setFile] = useState(post?.image);
  const { register, handleSubmit, control, errors, reset } = useForm({
    resolver: yupResolver(postSchema),
  });

  const onSubmit = (data) => {
    const updatedPost = {
      _id: post._id,
      ...data,
      image: file,
    };
    dispatch(updatePost(post._id, updatedPost));

    reset();
    setFile(null);
    closeEditMode();
  };

  const classes = useStyles();
  return (
    <div>
      <form noValidate autoComplete="off" onSubmit={handleSubmit(onSubmit)}>
        <TextField
          id="title"
          label="Başlık"
          name="title"
          variant="outlined"
          className={classes.textField}
          size="small"
          {...register('title')}
          error={errors?.title ? true : false}
          fullWidth
          defaultValue={post?.title}
        />
        <TextField
          id="subtitle"
          label="Alt Başlık"
          name="subtitle"
          variant="outlined"
          className={classes.textField}
          size="small"
          {...register('subtitle')}
          error={errors?.subtitle ? true : false}
          fullWidth
          defaultValue={post?.subtitle}
        />
          <Controller 
            render={({field}) => (
                <Select
                    {...field}
                    input={<Input />}
                    className={classes.textField}
                    fullWidth
                >
                    {
                        tags.map((tag, index) => (
                            <MenuItem {...field} key={index} value={tag}>
                                {tag}
                            </MenuItem>
                        ))
                    }
                </Select>
            )}
            name='tag'
            control={control}
            error={errors?.tag ? true : false}
            defaultValue={tags[0]}
        />
        <TextField
          id="content"
          label="İçerik"
          name="content"
          multiline
          size="small"
          {...register('content')}
          rows={16}
          className={classes.textField}
          variant="outlined"
          error={errors?.content ? true : false}
          fullWidth
          defaultValue={post?.content}
        />
        <FileBase64 multiple={false} onDone={({ base64 }) => setFile(base64)} />
        <div className={classes.buttons}>
          <Button color="primary" variant="outlined" onClick={closeEditMode}>
            Vazgeç
          </Button>{" "}
          <Button color="secondary" variant="outlined" type="submit" >
            Kaydet
          </Button>
        </div>
      </form>
    </div>
  );
};

export default EditPostForm;

I have EditPostForm component, component doesn't give any error but when I tried to submit my form onSubmit function is not triggered.

I used react-hook-form to create my form and I used material UI components inside form. When I Click button which has type submit does not trigger onSubmit function which is called inside of handleSubmit. Why onSubmit is not triggered?

Upvotes: 33

Views: 51136

Answers (7)

Andrey Popov
Andrey Popov

Reputation: 7510

A bit late to the party, but it took me an hour to figure this out.

The submit happens ONLY if there are no validation errors. In my case - I've renamed a field, and therefore validation was failing. The only way to understand that is to use the second parameter of handleSubmit - be careful, as I thought error is the second parameter of onSubmit (which is the first parameter of handleSubmit):


function onSubmit(values) {} // would be called ONLY if there's no validation error

function onError(errors, event?) {} // get to know what the issue is

<form onSubmit={handleSubmit(onSubmit, onError)}
</form>

Upvotes: 1

Minh Ho
Minh Ho

Reputation: 21

As others have said, react hook form would not trigger the submitHandler if there are still errors within the formState.errors object. It would automatically go to the error handler of the handleSubmit parameters. I have done a makeshift workaround that would ignore this case:

  <FormProvider {...methods}>
  <Card className={classNames('p-0 md:py-2 lg:px-10', wrapperCardClass)}>
    <LoadHandler status={recordStatus} errors={recordErrors}>
      <form
        className="relative flex flex-col gap-4"
        onSubmit={(e) => {
          methods.clearErrors()
          methods.handleSubmit(onSubmit)(e)
        }
      >

This would clear the errors before handling the submit request. I've used this for my project which is all server-side validation. Hope this answer helps!

Upvotes: 2

Hariharan L
Hariharan L

Reputation: 1411

For someone who doesn't get this resolved by other solutions mentioned here. Two other possible issues.

  1. There was no type="submit" attribute in the Button component.
  2. The html form element didn't have the input fields and button component in the actual dom. The form contents were rendered inside a modal div in a different node in the DOM tree. Even though we see the form contents as children of FormProvider, the actual DOM fields were rendered as portal. So, actual DOM looked like

React DOM

const FormContent = () => {
  return (
    <>
      <Fields />
      <Button />
    </>
  );
};

const CreateItemButton = () => {
  return (
    <>
      <Button onClick={openModel}>Create</Button>
      <FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
        <Modal isOpen={isOpen} onClose={closeModal}>
          <FormContent />
        </Modal>
      </FormProvider>
    </>
  );
};

HTML: (Actual DOM)

<!-- actual page -->
<button>Create</button>
<form></form>

<!-- somewhere down the line as portal -->
<div class="modal">
  <input />
  <input />
  <button />
</div>

Upvotes: 7

Panchakshari Puranmatt
Panchakshari Puranmatt

Reputation: 333

Basically the button submits only when all the validation conditions are met(if any) and if there are no field errors. So, its better to check if there are any errors being generated after filling all the fields or while clicking on submit.

Upvotes: 0

Aamir Hussain Padder
Aamir Hussain Padder

Reputation: 167

You need to pass onSubmit and onError both.

Like this:

onPress={handleSubmit(onSubmit, onErrors)}

Upvotes: 14

Ndatimana Gilbert
Ndatimana Gilbert

Reputation: 329

I faced the same error, the problem was that, my register were Boolean and my input was string, and since the value was not required It didn't show errors until I figure out the problem and change register from Boolean to string

Upvotes: 2

Ivan Stelmakh
Ivan Stelmakh

Reputation: 644

onSubmit isn't triggered because you may have form errors

You can get errors from formState object (const { formState } = useForm(...)) And then use error={formState.errors?.content ? true : false} in your code https://react-hook-form.com/api/useform/formstate

See an example here https://codesandbox.io/s/keen-burnell-2yufj?file=/src/App.js

Upvotes: 56

Related Questions