48

I have a form in one of my React components, and and in the outside component that calls it I want to pass a reference to a button there, so that I can also submit that using that button.

To make it more clear I have the following:

import React, { Component } from "react";
import ReactDOM from "react-dom";

class CustomForm extends Component {
  render() {
    return (
      <form onSubmit={alert('Form submitted!')}>
        <button type='submit'>Inside Custom</button>
      </form>
    );
  }
}

function App() {
  return (
    <div>
      <CustomForm />
      <button>In Root</button>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Now, I can submit the form using the button titled 'Inside Custom', but I also want to be able to submit the form using the button titled 'In Root' that is located in the root component. Is there a way to somehow pass reference from that button to that custom component, and actually submit the form when In Root button is clicked?

tinker
  • 2,884
  • 8
  • 23
  • 35
  • 7
    Can't we just use `form` attribute on button? https://www.w3schools.com/tags/att_form.asp – yurzui Sep 30 '18 at 16:54
  • Possible duplicate of [Submit Form using Button in Parent Component in React](https://stackoverflow.com/questions/44062024/submit-form-using-button-in-parent-component-in-react) – Pavlo Kyrylenko Dec 03 '18 at 10:31

7 Answers7

147

You can achieve this by using regular HTML capabilities (HTML form Attribute), no need to use the React hacks:

Add "id" attribute to your form: id='my-form'

class CustomForm extends Component {
    render() {
        return (
             <form id='my-form' onSubmit={alert('Form submitted!')}>
                // Form Inputs go here    
             </form>
        );
    }
}

Then add the same Id to the "form" attribute of the target button outside of the form:

<button form='my-form' type="submit">Outside Button</button>

Now, the 'Outside Button' button will be absolutely equivalent as if it is inside the form.

Note: This is not supported by IE11.

Pubudu Dodangoda
  • 2,742
  • 2
  • 22
  • 38
Pavlo Kyrylenko
  • 2,355
  • 2
  • 21
  • 21
11

Edit: Simple and correct answer: https://stackoverflow.com/a/53573760/5271656

In React, data flows down and actions flow up. So notify child component about button click in the parent.
This is how you can do this.

import React, { Component } from "react";
import ReactDOM from "react-dom";

class CustomForm extends Component {
  handleOnSubmit = e => {
    e.preventDefault();
    // pass form data
    // get it from state
    const formData = {};
    this.finallySubmit(formData);
  };

  finallySubmit = formData => {
    alert("Form submitted!");
  };

  componentDidUpdate(prevProps, prevState) {
    if (this.props.submitFromOutside) {
      // pass form data
      // get it from state
      const formData = {};
      this.finallySubmit();
    }
  }

  render() {
    return (
      <form onSubmit={this.handleOnSubmit}>
        <button type="submit">Inside Custom</button>
      </form>
    );
  }
}

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      submitFromOutside: false
    };
  }
  submitCustomForm = () => {
    this.setState({
      submitFromOutside: true
    });
  };

  componentDidMount() {
    console.log(this.form);
  }

  render() {
    return (
      <div>
        <CustomForm submitFromOutside={this.state.submitFromOutside} />
        <button onClick={this.submitCustomForm}>In Root</button>
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);  

To me, this solution is hacky and not in a react way but serves your use-case.
Find working solution here:https://codesandbox.io/s/r52xll420m

Murli Prajapati
  • 8,833
  • 5
  • 38
  • 55
  • 1
    BTW, why do you say it's not a react way and is a hacky solution? – Bhojendra Rauniyar Sep 30 '18 at 11:30
  • but i didn't get why you want to have submit functionality inside CustomForm component, so why you can't simply make it in App component and pass as a prop to Custom form component? – shyam agarwal Sep 30 '18 at 11:41
  • @BhojendraRauniyar the correct way would have been to pass a function to child and child would call that on submit. But to answer OP's use-case that's the only solution I can think of. `ref` can also be used but that would require sending form values to parent – Murli Prajapati Sep 30 '18 at 11:51
3

Well, set up a button with type of submit and attribute form which has value equal to id of the form you want to submit

Jansindl3r
  • 360
  • 3
  • 18
2

You can set in the state a flag propery for example rootBtnClicked: false and a method that handles the click in the App component. Also in the <CustomForm />, add a prop e.g. rootBtnClicked={this.state.rootBtnClicked}. When "In Root" button is clicked, trigger that method; then the method changes the state with setState(). Then inside the CustomForm component's render() method, check if the prop is true. If it is, manually trigger .submit() using React's ref.

Emily
  • 191
  • 1
  • 4
  • 2
    You can try to provide a code sample when you answer questions. Just saying, it can help people better understand what you mean. – tinker Sep 30 '18 at 13:26
0

You can pass the submit function as a prop and use this method whenever you want. for more complex situations you can use redux save your form data on redux and whenever the button is clicked you read data from redux and submit them

Amir Tahani
  • 688
  • 5
  • 13
0

None of these solutions worked for me, but I finally figured it out by doing

document.forms[0].submit()

Since there was only one form on the page, I selected it with the 0 index, but if there are multiple forms then you'd need to use the applicable index for the forms array.

dmc85
  • 250
  • 1
  • 9
-2

You can pass the onSubmit click handler as a props as follows:

import React, { Component } from "react";
import ReactDOM from "react-dom";

class CustomForm extends Component {

  render() {
    return (
      <form onSubmit={this.props.handleSubmit}>
        <button type='submit'>Inside Custom</button>
      </form>
    );
  }
}

function App() {
 handleSubmit = () => {
   alert('Form submitted!')
}
  return (
    <div>
      <CustomForm onSubmit={this.handleSubmit} />
      <button>In Root</button>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Passing function as a props to handle form submit makes the CustomForm element more reusable since the business logic for the submit is not part of CustomForm component.

Raushan
  • 93
  • 6