jquense / react-formal

Sophisticated HTML form management for React
http://jquense.github.io/react-formal
MIT License
527 stars 52 forks source link

Nested form submits container form #158

Open rachelleahr opened 5 years ago

rachelleahr commented 5 years ago

I have a form that has a fieldArray in it - that field array uses a form inside with a submit button. Right now whenever the nested form gets submitted it also calls the container's onSubmit. Is that expected behavior or am I doing something wrong?

jquense commented 5 years ago

can you post some trimmed down code demonstrating the issue? thanks!

rachelleahr commented 5 years ago

Something like this

const contactsSchema = Yup.object().shape({
  contacts: Yup.array()
    .of(Yup.object())
    .test('containsPrimaryContact', 'Primary contact is required', values =>
      values.some(v => v.isPrimaryContact)
    )
});

...

this.state = {
      currentlyEditing: 0,
      contacts: [{ ...defaultContact }],
      errors: {}
};

....

<Form
            as="div"
            schema={contactsSchema}
            value={{ contacts }}
            onChange={() => {}}
            onSubmit={values => {
              console.log('form submitted');
              onSave(values);
            }}
          >
            <Form.FieldArray name="contacts" events="blur">
              {({ value, arrayHelpers }) => (
                <div>
                  {value.map((c, i) => {
                    if (i === currentlyEditing) {
                      return (
                        <Form
                          onSubmit={values => {
                            this.onSave(values, i);
                            this.setState({
                              submitted: true,
                              currentlyEditing: -1
                            });
                          }}
                          contact={c}
                          index={i}
                          onCancel={() => arrayHelpers.remove(c)}
                          key={i.toString()}
                          errors={errors}
                          setTouched={() => this.setState({ touched: true })}
                          onError={e => this.setState({ errors: e })}
                        >
                       {form fields}
                       <Form.Submit
                             as={Button}
                             isPrimary
                             width={154}
                             height={54.6}
                             label="Save"
                        />
                      </Form>
                      );
                    }
                    return (
                      <ClosedContact
                        isOpen
                        contact={c}
                        editContact={() => this.setState({ currentlyEditing: i })}
                        index={i}
                      />
                    );
                  })}
                  {currentlyEditing >= 0 ? null : (
                    <PlusIcon onClick={this.addContact} />
                  )}
                </div>
              )}
            </Form.FieldArray>
            <Form.Message  for="contacts" />
            <div >
              <Form.Submit
                as={Button}
                isPrimary
                width={154}
                height={54.6}
                label="Finish"
              />
            </div>
          </Form>
rachelleahr commented 5 years ago

I took a look at some of the source code and I can't find where the NestedForm component is exported - I was using the regular form, maybe that's the problem?

jquense commented 5 years ago

so What i think may be happening is the native submit event is bubbling up in each form, but it's a bit hard to know without a test case. I would first make sure you are actually nesting html <form> elements, so the inner Form should set the the as prop to something like a div.

rachelleahr commented 5 years ago

Thanks for the quick response - that seems to have done the trick! 👍 switched all forms to use divs instead

thejohnfreeman commented 5 years ago

Just now discovering this library. Wish I knew about it before embarking on my own. I would love to join forces.

@jquense, I haven't dug into your code yet, but if you're doing it like you do in topeka (and like I am), then you're providing a context in the Form component. The way I solved this problem is to add that context to the Form itself, use null as the default, and then check in the Form.render method whether the context is non-null and conditionally wrap the children in a <form> HTML element.

rachelleahr commented 5 years ago

@thejohnfreeman I'm confused as to how this solves my problem? or was that just a general suggestion?

thejohnfreeman commented 5 years ago

Sorry, @racheldelman, it's a suggestion to @jquense for how to fix this problem in the library, not for you as a user.

rachelleahr commented 5 years ago

oh ok thanks! as I said before the NestdForm doesn't even seem to be exported anywhere so I assume this is more to fix there as well

jquense commented 5 years ago

thats a good idea @thejohnfreeman for setting the default inner element 👍