I'm getting trouble to understand the following situation in ReactJS.
I have a conditional rendering between two buttons: a button which shows another, basically. The second button is a submit type, and both buttons are inside the form. When I click at the first button to show/render the second one, in my understanding, it should just show the second button, and not submit the form automatically.
I reproduced the case with the create react app:
function App() {
const [showSubmit, setShowSubmit] = useState(false);
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<form method="POST" action="#">
{showSubmit ? (<button type="submit">Send</button>)
:
(<button type="button" onClick={() => setShowSubmit(true)}>
Show Submit
</button>)}
</form>
</header>
</div>
);
}
Why does the ReactJS automatically fires the submit event if I just want to show/render the submit button?
Posting here for anyone in the future who encounters this issue (probably me) - the issue you've described can be remedied by adding a key prop to the button elements, to let react distinguish between the button elements you're replacing.
Handle the form onSubmit function prevent the default function call, when ever you try to click on any button inside the form it will try to submit the form so use the e.preventDefault() to prevent it from submitting.
You can try handling the submission yourself using the onSubmit on the form tag instead of letting the browser do it for you.
import React, { useState } from 'react';
import './style.css';
function App() {
const [showSubmit, setShowSubmit] = useState(false);
console.log('state', showSubmit);
return (
<div className="App">
<header className="App-header">
<img src={'logo'} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<form method="POST" action="#" onSubmit={(e) => e.preventDefault()}>
{showSubmit ? (
<button type="submit">Send</button>
) : (
<button type="button" onClick={(e) => setShowSubmit(true)}>
Show Submit
</button>
)}
</form>
</header>
</div>
);
}
export default App;
Related
I have a modal that when it is closed updates a state. The modal also has a div which will be replaced if a button is clicked. The button is replaced with a text area and 2 buttons (one of them a cancel.) If the cancel is clicked, the state updates and the text area hides. All good. However, if the user closes the modal, then the state is not updated and the div is shown next time.
I am unsure of how to set 2 states on close for the modal, I think this could sort this issue.
Code has been updated as per #jsNoob suggestion:
Hint component has
const [showModalProblemS_vid, setShowModalProblemS_vid] = useState(false);
<VideoModal showModal={showModalProblemS_vid} closeHandler={() => setShowModalProblemS_vid(false)} videoMessage={props.prob_s_vid} size='med'/>
So how to set a state which is not in the file is the issue
Modal Code below:
import Button from 'react-bootstrap/Button';
import Modal from 'react-bootstrap/Modal';
import { useState } from 'react';
function VideoModal({showModal = false, closeHandler = () =>{}, videoMessage, content, size}) {
const [confused, setConfused] = useState(false)
return (
<Modal
size={size}
show={showModal}
onHide={closeHandler}
onClose={()=> {setConfused(false); closeHandler()}}
backdrop="static"
keyboard={false}
>
<Modal.Body>
<video src={videoMessage} controls autoPlay></video>
<div>
{confused ? (
<div>
What have you found confusing about this video?
<textarea className='confusedText' rows="2"></textarea>
<Button className="confusedBtnSave">
Save
</Button>
<Button className="confusedBtnCancel" onClick={()=>setConfused(false)}>
Cancel
</Button>
</div>
) : (
<div>
<Button className="confusedBtn" onClick={()=>setConfused(true)}>
Confused?
</Button>
</div>
)}
</div>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={closeHandler}>
Close
</Button>
</Modal.Footer>
</Modal>
)
}
export default VideoModal
Well to set multiple states you can do that inline
<Button onClick={() => {setState1(foo); setState2(bar)}}></Button
Or you could add them to a function and then call the function inline
function multipleStates(foo, bar){
setState1(foo)
setState2(bar)
}
<Button onClick={() => multipleState(foo, bar)}></Button>
I'm not sure if that is too broad of an answer for you, hopefully I haven't misinterpreted your question.
Got this working. I had to use the following:
onClick={() => {onClose(); setConfused(false)}}
note the semi-colon between the states
TLDR: Cannot figure out why component is still being rendered while no props are passed.
So I have been building a NextJS application, and I have this banner component that is shown on every page of my website. It has some header text, buttons and an image:
const Banner = (props) => {
return (
<div className={bannerStyles.wrapper}>
<div className={classnames(bannerStyles.banner, "wrap", "center")}>
<div className={bannerStyles.banner_left}>
<h1>{props.header}</h1>
<div className={bannerStyles.button_wrapper}>
<div className={bannerStyles.button}>
<Button>{props.button || null}</Button>
</div>
<div className={bannerStyles.button}>
<Button>{props.scnd_button || null}</Button>
</div>
</div>
</div>
<div className={bannerStyles.banner_right}>
<Image src={props.image} alt=""></Image>
</div>
</div>
</div>
);
};
Inside of this, as you can see I have two Button components (The MDEast thing is an arrow icon):
const Button = ({children}) => {
return (
<div className={buttonStyles.button}>
<Link href="/"><a>{children} <MdEast /></a></Link>
</div>
)
}
Now I want the option that if no prop is passed, that the Button component(s) do(es) not render/ is hidden from the page, so that it is optional per page. Yet the Button does still render, even though I am not passing any props on my About page. My about page:
const About = () => {
return (
<>
<Banner
header="Hello this is my code"
image={banner_placeholder}
/>
</>
)
}
PS. I am fairly new to React and NextJS, so this might be a beginner mistake, or I am not understanding the fundamentals well enough, but could someone point me in the right direction please?
To conditionally render the button you can use:
props.button && <Button>{props.button}</Button>
When props.button is falsy, then button will not get rendered.
For some reason, in my code the onClick event for the disableAccount button gets activated whenever I click the cancel button. So cancel sets isEditMode to false but disableAccount sets it back to true thus preventing me from seeing any noticeable change in my UI. This is a next.js project.
import React from 'react';
import TextInput from '../Input/TextInput';
import SmallButton from '../Buttons/SmallButton';
const DisableAccountForm = ({t, tc, onSubmit, password, setPassword, isEditMode, setIsEditMode}) => {
return (
<label className="block w-full max-w-xl text-2xl mb-4">
{t("admin")}
<form onSubmit={onSubmit} className="w-full max-w-xl text-base mt-2">
<p className="mb-4">
{t("disableP")}
</p>
{isEditMode ?
<div>
<p className="mb-4">
{t("youSureP")}
</p>
<p className="text-sm mb-4">
{t("enterPasswordP")}
</p>
</div>
: null
}
{isEditMode ?
<label className="flex-col">
{tc("password")}
<TextInput
placeholder={tc("password")}
value={password}
onChange={e=>setPassword(e.target.value)}
style="m-1"
required
/>
<SmallButton
type="submit"
label={tc("submit")}
color='bg-blue-500'
textColor='text-white'
/>
<SmallButton
type="button"
label={tc("cancel")}
color='bg-red-500'
textColor='text-white'
onClick={() => {console.log("cancel"); setIsEditMode(false)}}
/>
</label>
:
<SmallButton
type="button"
label={t("disableAccount")}
color='bg-blue-500'
textColor='text-white'
onClick={() => {console.log("why"); setIsEditMode(true)}}
/>
}
</form>
</label>
);
}
export default DisableAccountForm;
This is the controller for the code above.
import React, {useState} from 'react';
import DisableAccountForm from '../components/DisableAccountForm/DisableAccountForm';
const DisableAccountController = ({t, tc, onSubmit}) => {
const [password, setPassword] = useState("");
const [isEditMode, setIsEditMode] = useState(false);
const disableAccount = (e) => {
e.preventDefault();
console.log("disableAccount");
setIsEditMode(false);
}
return (
<DisableAccountForm
t={t}
tc={tc}
onSubmit={disableAccount}
password={password}
setPassword={setPassword}
isEditMode={isEditMode}
setIsEditMode={setIsEditMode}
/>
);
}
export default DisableAccountController;
SmallButton
import React from 'react';
const SmallButton = ({type, label, color, textColor, style, onClick}) => {
return (
<button
type={type}
className={`m-1 ${color} hover:ring-2 rounded-md px-2 py-1 min-w-20 ${textColor} ${style}`}
onClick={onClick}
>
{label}
</button>
);
}
export default SmallButton;
TL;DR
The problem is that the label wrapper "clicks" the Disable account button right after the form is switched from edit to display mode. It is the default behaviour of HTML label elements. You can solve it by changing the label to div or fragment or any other wrapper that does not click your buttons.
Explain In Details
Go to the sandbox you have posted. It opens in display mode (not edit mode). Now, press the text "Admin" on the top. As you can see, the "Disable account" button is pressed - surely not the expected behaviour.
This unexpected behaviour happens for the same reason that the cancel button switches back to edit mode. the whole problem is about the label that wraps the form. The HTML native label element attaches the click event of one of the clickable elements nearby or inside the label to the content inside the label. In-display mode, the label detect the "Disable account" button and attach its click event to the whole component.
So, when you click the "cancel" button - the component switches to edit mode, and the label wrapper "clicks" the "Disable account" button and switches it back to edit mode. You don't see it when submitting the form, because you prevent the label's default behaviour in onSubmit callback. A partial solution for your problem can be to preventDefault in the cancel callback too - but it will leave the texts clickable. Another solution might be to change the label to div or fragment or any other wrapper that does not click your buttons.
Is there a way to stop the buttons activating the onClick? I've tried zIndexes, but they are not working.
return (
<div onClick={()=>history.push("/page")}>
<main className={classes.layout}>
<Paper className={classes.paperN}>
<div>
<h1> Examples </h1>
</div>
<div className={classes.theButtons}>
<Button> When I'm clicked, the onclick to new page occurs </Button>
</div>
</div>
</Paper>
</main>
</div>
);
There are two ways to do this.
One is to stop the propagation and/or default action of the button.
something like
<Button onClick={(e)=>{e.stopPropagation();e.preventDefault();}}> When I'm clicked, the onclick to new page occurs </Button>
the other is to detect if the click originated in a button and do nothing in your redirect handler.
<div
onClick={(e) => {
if (!e.target.closest("button")) {
history.push("/page");
}
}}
>
I have issue with my final form. I have a Modal with a react-final-form in it and would like to submit the form with the button which is in the footer of the modal. Submit button has onSubmit event, when I clicked this button I saw that my onSubmit function doesn't work. It works when I click button which opens modal.... What is going wrong here?
Advert.js
class Advert extends React.Component {
showLoginMenu = (e) => {
e.preventDefault();
this.props.loadModal(LOGIN_MODAL);
};
....
<button onClick={this.showLoginMenu.bind(this)}>Order</button>
}
Modal.js
<Modal onClose={this.onClose.bind(this)}>
<Form
onSubmit={this.onSubmit}
initialValues={initValues}
decorators={[calculator]}
render={({handleSubmit}) => (
<form onSubmit={handleSubmit}>
<some fields>
<button type="submit" onSubmit=
{this.onSubmit}>Order</button>
</form>
)}
/>
As far as I know, onSubmit is a prop of form tag, not button tag. You should use onClick instead.
I know this has probably been resolved by now as this issue is about 2 years old.
But to resolve this issue you need to include the Modal inside of the Form component, but before the form tag
<Form
onSubmit={this.onSubmit}
initialValues={initValues}
decorators={[calculator]}
render={({handleSubmit}) => (
<Modal onClose={this.onClose.bind(this)}>
<form onSubmit={handleSubmit}>
<button type="submit" onSubmit={this.onSubmit}>Order</button>
</form>
<Modal>
)}
/>