React JS, two submit buttons in one form
Asked Answered
S

6

24

When using React JS, how can I identify which button was used to submit the form?

I thought something like this would work, but it doesn't:

export default function App() {
  const onSubmit = e => {
    e.preventDefault();
    console.log(e.target.btn.value);
  };

  return (
    <form className="App" onSubmit={onSubmit}>
      <button type="submit" name="btn" value="wow">
        Button 1
      </button>
      <button type="submit" name="btn" value="oh no">
        Button 2
      </button>
    </form>
  );
}

Code sandbox

According to standard HTML you should be able to name two buttons the same name? Or use formaction attribute to distinguish them.

In my use case the buttons don't have knowledge about the form. I want to avoid a solution where I use some temporary state to remember which button was clicked.

In standard HTML you can do this:

<form action="/action_page.php">
  <input type="submit" name="btn" value="Submit">
  <input type="submit" name="btn" value="Submit2">
</form> 

When you submit the form btn will either be posted Submit or Submit2 depending on which button you click. I want to get as close as possible to this standard when building my React form. Use as little help from Javascript and React as possible. Basically just add the buttons to the DOM inside my form and collect the values from the event that I have access to inside of the submit handler.

Shebat answered 22/2, 2020 at 6:50 Comment(5)
Why not use onClick for each button? You can have a separate handler for each, or pass an argument to a shared handler.Autosuggestion
what are you trying to achieve? why do you want to use 2 submit buttons?Sanies
I want to add buttons in a declarative way and then collect the form data that was submitted. In my use case the buttons have no knowledge about the form. I want to work with the form in a standard way, but I want to intercept the submit event and post the data myself with an AJAX request, and not use the form action and method.Shebat
Which solution did you go for in the end?Anneliese
@ManuelJacob I went with a state within the component that I toggle.Shebat
C
27

Try using event.submitter.name property. Add different name to your buttons and check their names in eventHandler. If you use some kind of library for making forms it may lay in e.nativeEvent.submitter.name
More info https://developer.mozilla.org/en-US/docs/Web/API/SubmitEvent/submitter

Cremate answered 23/2, 2021 at 13:50 Comment(5)
4got about nativeEvent 👍🏾Melee
Are there any negatives in using nativeEvent.submitter.name like it may not work on certain browsers?Essentialism
it s not supported by safari .Comma
@Essentialism Yes the main problem is that it's not supported below Chrome v81 and FF v75. I hit a Javascript error for this with someone using an old Chrome browser (v77), so make sure you have a fallback for it.Sodium
@Essentialism Note that it's the whole SubmitEvent interface that was introduced - which is what the e.nativeEvent should be. I don't know what the React libraries fallback to prior to Chrome v81.Sodium
S
1

Working with forms in ReactJS is rather straight forwards, if you know how input works. Here is some more information directly from the documentation: https://reactjs.org/docs/forms.html

And here is a small example which I will explain for you:

      // You need to add a onSubmit event to the form. This will trigger when you submit the for as you do any other html form (including pressing the submit button)
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          // need to save input value somewhere (use the state for this, check the link from above
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label>
        // this is your button, as with other html forms.
        <input type="submit" value="Submit" />
      </form>

In the case you described, you shouldn't really submit the data inside the buttons (why would you do that?), but instead submit the form data inside inputs. If you really want two buttons for submit (although a form can be submitted by hitting the key enter for example), you can do so like this:

    // onSubmit is the same as before, handle form data here
    <form className="App" onSubmit={onSubmit}>
      // setSubmitButton would set the submit button to button1 (e.g. use react state for this)
      <button type="submit" onClick={setSubmitButton('button1')} name="btn" value="wow">
        Button 1
      </button>
      <button type="submit" onClick={setSubmitButton('button2')} name="btn" value="oh no">
        Button 2
      </button>
    </form>
Sanies answered 23/2, 2020 at 7:43 Comment(5)
I have a use case where I have different behaviours. That is the user can save the form data as a draft or they can decide to publish the changes. I don't find that so strange actually. I work with all my fields as uncontrolled inputs. I don't store their state anywhere because they can control their internal state themselves. I would like to achieve that with the buttons as well. It works with standard HTML and now I'm trying to achieve the same with React and some Javascript, but apparently I cannot extract it from the submit event and that has little to do with React I think.Shebat
I have no troubles find a working solution (remember which button was clicked using React state or ref) but I want to find one that is as close as possible to standard HTML forms.Shebat
@Shebat ok, so you are trying to replicate something like makandracards.com/makandra/… in React with as little React as possible? I didn't understand that from your initial post, sorry.Sanies
Exactly :) But it seems to be a limitation of how the submit event works in Javascript. All my input fields they are there, but if two inputs (the use case I have with buttons) have the same name then it is not possible to resolve a value. And for regular inputs there shouldn't be and the buttons seems to be treated the same way. I think I have to fallback to some solution along the way you suggested but I'm still seeking for another way to do it.Shebat
@Shebat I understand that, but I'd try and make it closer to React and just use onClick instead (will be easier to maintain in the future as well). What is it that you are afraid will not be possible if you use onClick on each button?Sanies
T
1

You can determine which button was clicked by checking which element currently has focus using document.activeElement in the onSubmit function. Add different ids or data attributes to the buttons to determine which one was clicked.

Twosided answered 29/8, 2021 at 14:45 Comment(0)
T
1
e.nativeEvent.submitter.name;

Worked for me.

Tease answered 30/8, 2023 at 12:32 Comment(0)
B
0

Either you can try the below code or you can try to make separate calls for both buttons.

Demo link codesandox

import React from "react";
import "./styles.css";

export default function App() {
  const state = {
    button: 1
  };

  const onSubmit = e => {
    e.preventDefault();
    if (state.button === 1) {
      console.log("Button 1 clicked!");
    }
    if (state.button === 2) {
      console.log("Button 2 clicked!");
    }
  };

  return (
    <form className="App" onSubmit={onSubmit}>
      <button
        onClick={() => (state.button = 1)}
        type="submit"
        name="btn1"
        value="wow"
      >
        Button 1
      </button>
      <button
        onClick={() => (state.button = 2)}
        type="submit"
        name="btn2"
        value="oh no"
      >
        Button 2
      </button>
    </form>
  );
}
Bellinzona answered 22/2, 2020 at 7:15 Comment(7)
This is absolutely wrong. Please look into 'useState' and other hooks if you want to learn about how to keep state inside a functional react component. reactjs.org/docs/hooks-state.htmlSponger
However this example is working in my case.Starrstarred
This works, because whenever you click the button, component is not rerendered by React,, so object reference is still the same, during the onClick and onSubmit event. If anything would cause React to render this component again between onClick and onSubmit this would not work.Intracardiac
@Sponger - Only the name "state" is confusing here, this is not a react state in actual but just a global variable to store the value of the selection. It could just be let buttonNum = 1 and then changed in clickNephrotomy
@ArvindK. This is a local variable, not a global one, and that's just one of the reasons why this is faulty.Sponger
This is a great answer, if you use it appropriately (in class or function react component). Just get the basic logic and do modifications in your code. That way worked for me.Sollars
@Sponger why not post an answer of using useState so we can all learn from different answers? much better than critiquing another answer in my opinionStroud
K
0

I use e.nativeEvent.submitter. As long as react/nextjs do not support it, I use the following kludge to make typescript happy:

function getSubmitter(e: Event & { submitter?: HTMLButtonElement} ):
    HTMLButtonElement|undefined
{
    return e?.submitter;
}

function example(): JSX.Element {
    const handleSubmit = async (e:  React.FormEvent<HTMLFormElement>):
        Promise<void> =>
    {
        e.preventDefault();
        var btn = getSubmitter(e.nativeEvent);
        if (btn?.id == 'clear') {
            clearScreen();
            return;
        }
        //...
    }

    return (
        <form action='/check' method='post' onSubmit={handleSubmit}>
            <button type="submit" id="clear">Clear Screen</button>
            <button type="submit" id="store">Store</button>
            // ...
        </form>
    )
}
Kellie answered 1/5, 2023 at 8:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.