I'm trying to conditionally switch between two HTML buttons in ReactJS based on wether the form is shown or not. Basically if the form is shown, show a submit button and if not shown, show a regular button to show the form.
This is the code that I have. (I'm using Antd's button component but I also tried using the regular HTML button and had the same problem.
<div>
{showForm ? (
<Button
loading={isUploading}
htmlType="submit"
form="videoForm"
className={Styles.button}
>
{isUploading ? 'Uploading' : 'Upload Video'}
</Button>
) : (
<Button
htmlType="button"
onClick={() => setShowForm(true)}
className={Styles.button}
>
{successMsg ? 'Upload Another Video' : 'Show Form'}
</Button>
)}
</div>
The issue I'm having is that when the form is not shown and I click the Show Form button, the form shows correctly and the buttons switch, but the form submit event is triggering, which is not what I expected or want.
Any ideas why? And how I can fix this issue? I also tried doing the following but got the same results.
<div>
<Button
loading={isUploading}
htmlType={showForm ? 'submit' : 'button'}
form="videoForm"
className={Styles.button}
>
{showForm && (isUploading ? 'Uploading' : 'Upload Video')}
{!showForm && (successMsg ? 'Upload Another Video' : 'Show Form')}
</Button>
</div>
Any help would be greatly appreciated.
This behaviour is unrelated to the default type
of the <button>
, as OP is already setting the underlying HTML <button>
element's type
to 'button'
via the htmlType
prop.
This behaviour can also be reproduced without React, as illustrated below:
const form = document.getElementById('form'),
btnWo = document.getElementById('btn-wo'),
btnW = document.getElementById('btn-w');
form.onsubmit = function (event) {
console.log('form onsubmit');
event.preventDefault();
}
btnWo.onclick = function (event) {
console.log(`btn-wo onclick, type = ${this.type}`);
}
btnW.onclick = function (event) {
console.log(`btn-w onclick, type = ${this.type}`);
this.type = 'submit';
}
<form id="form">
<button id="btn-wo" type="button">Button without Changing Type</button>
<button id="btn-w" type="button">Button with Changing Type</button>
</form>
When "Button with Changing Type" is clicked, its type
is changed to 'submit'
in the event handler. After that event handler returns, the form is submitted because the clicked <button>
's type
is now 'submit'
.
The same happens in OP's example because React doesn't remount the <Button>
component (and the underlying HTML <button>
element), only the htmlType
prop (and the type
property of the underlying element) is changed.
To fix that, either:
preventDefault()
inside the event listener. This will prevent the default action of clicking a submit <button>
associated with a form, which is submitting the form.const { useState } = React;
const App = () => {
const [showForm, setShowForm] = useState(false);
const onSubmit = (event) => {
console.log("form onSubmit");
event.preventDefault();
setShowForm(false);
};
return (
<div>
<div>
{showForm ? (
<button type="submit" form="videoForm">
Upload Video
</button>
) : (
<button
type="button"
onClick={(event) => {
setShowForm(true);
event.preventDefault();
}}
>
Show Form
</button>
)}
{showForm && (
<form id="videoForm" onSubmit={onSubmit}>
<input />
</form>
)}
</div>
</div>
);
};
ReactDOM.createRoot(document.getElementById("root")).render(<App />);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<button>
's type
to 'submit'
after a timeout. This works because the <button>
's type
is still 'button'
at the moment the event handler returns.const { useState } = React;
const App = () => {
const [showForm, setShowForm] = useState(false);
const onSubmit = (event) => {
console.log("form onSubmit");
event.preventDefault();
setShowForm(false);
};
return (
<div>
<div>
{showForm ? (
<button type="submit" form="videoForm">
Upload Video
</button>
) : (
<button type="button" onClick={() => setTimeout(() => setShowForm(true))}>
Show Form
</button>
)}
{showForm && (
<form id="videoForm" onSubmit={onSubmit}>
<input />
</form>
)}
</div>
</div>
);
};
ReactDOM.createRoot(document.getElementById("root")).render(<App />);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
type
of the <button>
; use 1 <button>
element for each type. In React, you can use the key
prop on one of the components/elements to achieve this.const { useState } = React;
const App = () => {
const [showForm, setShowForm] = useState(false);
const onSubmit = (event) => {
console.log("form onSubmit");
event.preventDefault();
setShowForm(false);
};
return (
<div>
<div>
{showForm ? (
<button type="submit" form="videoForm">
Upload Video
</button>
) : (
<button type="button" onClick={() => setShowForm(true)} key="s-btn">
Show Form
</button>
)}
{showForm && (
<form id="videoForm" onSubmit={onSubmit}>
<input />
</form>
)}
</div>
</div>
);
};
ReactDOM.createRoot(document.getElementById("root")).render(<App />);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>