Why onClick fires several times - javascript

I'm useing Semantic-ui-react redux and react-router-dom. In my component I have navbar:
<Menu >
<Menu.Item as={Link} to="/" onClick = {this.onNavBarItemCLick()}>
home
</Menu.Item>
<Menu.Item as={Link} to="profile" onClick = {this.onNavBarItemCLick()}>
profile
</Menu.Item>
<Menu.Item as={Link} to="shop" onClick = {this.onNavBarItemCLick()}>
shop
</Menu.Item>
</Menu>
If I click on the one of nav items method onNavBarItemClick fires 3 times. Why it happens?

Because you call method immediately, instead you should only pas handler to onClick attribute:
as={Link} to="shop" onClick = {this.onNavBarItemCLick.bind(this)}>

Solution
You need to replace
onClick = {this.onNavBarItemCLick()}
with
onClick = {() => this.onNavBarItemCLick()} // perserving class's this
or if you prefer the bind notation
onClick = {this.onNavBarItemCLick.bind(this)} // perserving class's this
else
onClick = {this.onNavBarItemCLick} // not perserving class's this
Why it happens
It happens because when you click render() is called again.
React is very efficient in re-rendering, so it's what it's supposed to happen.
The issue with your implementation was that instead of passing a method reference, you were passing the returned value of such method.

Because you should not call it right away in the onClick prop. use this
<Menu.Item as={Link} to="/" onClick = {this.onNavBarItemCLick}>
What ends up happening, is the onNavBarItemCLick being called every time the render function is called when state changes

The onClick prop takes a function as value so what's the problem here ?
<MyComponent onClick={this.handleClick()} />
using this.handleClick() instantly call the function and gives its return value to onClick. What you want here is to give the function itself to onClick like so
<MyComponent onClick={this.handleClick} />
But you'll probably get another error there if this.handleClick uses this as it is not automatically bound in a React class. There are ways to fix this like binding this in the constructor, using an array function for handleClick or directly bind this when passing the function to onClick like
<MyComponent onClick={this.handleClick.bind(this)} />
Going a bit further, you could also return a function in handleClick which here is totally useless. It's only to illustrate :
class MyComponent extends React.Component {
handleClick () {
return () => { console.log('I am triggered on click') }
}
render () {
// We call this.handleClick() which returns the function
// that'll be actually called on click.
return <div onClick={this.handleClick()} />
}
}
I hope this helps

When you do this.onNavBarItemClick(), what happens is that the parenthesis execute the function at that point. Whereas this.onNavBarItemClick will treat it as a variable.
So in the above piece of code, when you do onClick = {this.onNavBarItemClick()}, the function is getting called there itself without the user clicking on it. Whereas if you do onClick = {this.onNavBarItemCLick} the function gets invoked only if you click on it.
You can also do onClick = {()=>this.onNavBarItemClick()}. By doing this you are creating an anonymous function and passing it to onClick.
So you can do either of onClick = {this.onNavBarItemClick} or onClick = {()=> this.onNavBarItemClick()}

Related

onClick inline function works different from outside function

I have a newbie question that is probably really easily explained. I have an image that has an onClick property added to it. If I call an inline function to log something, it works as expected. It logs to the console whenever the image is clicked. However, whenever I have an outside function called there, it gets clicked every second which is because I have data coming in every second updating a part of the code.
My question is, why does the inline function work and the other way doesn't? What am I missing here?
inline version...
return(
<>
<SettingsButton src="./img/leaf.png" alt="" onClick={() => { console.log("button clicked");}}/>
</>
);
and the other way that doesn't work as intended...
function handleClick() {
console.log('The link was clicked.');
}
return(
<>
<SettingsButton src="./img/leaf.png" alt="" onClick={handleClick()}/>
</>
);
In your example, you are calling function as soon as the component is loaded by writing it as handleClick(), so the browser does not wait for you to click as it should be.
handleClick =()=> {
console.log('The link was clicked.');
}
return(
<>
<SettingsButton src="./img/leaf.png" alt="" onClick={handleClick}/>
</>
);

How to only activate the onclick event of the top level button when having nested buttons? React.js

I'll try to explain this further.
I have a material-UI List component, with ListItem component that is set to button=true thus makes the whole item a button.
inside I added that inside him I have a FontAwesomeIcon.
To hide the button I put it's style to visibility: "hidden" and the Icon to visibility: "visible" so it would be available to see. (little bad practice maybe, but did not though of another way).
Now, when someone presses the ListItem anywhere without the Icon, it activates the onClick of that ListItem - as it should, and it's good! but, when pressing the area where the Icon is, both OnClick events of the "Icon button" and the ListItem is called - as it should, but I don't want it to be that way.
Now, is there a way to make the small "nested" button to be "on top" of the parent button so only it's event would be called?
If not, is there a way to know from the parent onClick that it's pressed on the area without the Icon so I would call different functions based on the click area?
Also, any other idea will be gladly received as I am new to react and web in general and I'd want to have the best practices solutions.
Many thanks :)
This is unrelated to React. In JavaScript you can use event.stopPropagation() method to stop the propogation of event at any level.
https://www.w3schools.com/JSREF/event_stoppropagation.asp#:~:text=Definition%20and%20Usage,capturing%20down%20to%20child%20elements.
Here is the example of how you would do it in React
import React from "react";
import "./styles.css";
export default function App() {
const parentButtonHandler = () => {
console.log("parent");
};
const childButtonHandler = (e) => {
console.log("child");
e.stopPropagation();
};
return (
<div className="App">
<button onClick={parentButtonHandler}>
Hello CodeSandbox
<button onClick={childButtonHandler}>
Start editing to see some magic happen!
</button>
</button>
</div>
);
}
If I understand your question correctly, you got that issue because the event is bubbled.
You can read this for more information: https://javascript.info/bubbling-and-capturing
To solve it, you can use event.stopPropagation() in the event handler for click event on "Icon button", so the event wont be bubbled to the parent element which is the ListItem
I think it's bad idea to make nested buttons. it's harder to support and it makes your layout messy.
In your case you can do it based on few ideas:
You have two separate buttons in your ListItem
export const Comp = () => {
return (
<ListItem>
<button onClick={handleOnMainClick}>mainButton</button>
<button onClick={handleOnSecondClick}>secondButton</button>
</ListItem>
)
}
But it works if your buttons on left side or right side only.
If you want to place your functional button whatether you want you can place it by position
export const Comp = () => {
return (
<ListItem styles={{position: 'relative'}}>
<button onClick={handleOnMainClick}>mainButton</button>
<button
styles={{position: 'absolute', top: '50%', left: '50%'}}
onClick={handleOnSecondClick}>
secondButton
</button>
</ListItem>
)
}

Why do React event handlers behave differently when receiving arguments?

With the following code, every time I click the button, "I was clicked" is outputted to the console, as expected.
function handleClick() {
console.log("I was clicked")
}
function App() {
return (
<div>
<img src="https://www.fillmurray.com/200/100"/>
<br />
<br />
<button onClick={handleClick}>Click me</button>
</div>
)
}
However, if I change handleClick to take in a parameter, the output is shown upon page load and any additional clicks don't produce additional output.
function handleClick(test) {
console.log(test + "I was clicked")
}
function App() {
return (
<div>
<img src="https://www.fillmurray.com/200/100"/>
<br />
<br />
<button onClick={handleClick("hello: ")}>Click me</button>
</div>
)
}
Why does the simple act of including a parameter change the behavior, here? I suspect it has to do with the event handler not passing along the event object in the second scenario, while automatically being passed in the first scenario, but I'm not sure what properties of the event object would be causing this behavior.
if you use () after the function; it means that the function executes. To assign values with this syntax, you can easily use
onPress={handleClick.bind(this, ...args)}
With this syntax, we bind the this of the current scope to the function and we did not execute it while assigning parameters. The way of assigning parameters and invoking the function immediately would be
handleClick.call(this, ...args)

onClick function not calling a method

I have a React project where a component SubmitButton is rendered when the property isSelectedNumber change its state to true. The component SubmitButton, which is a button you can click, will call a method when the button is clicked by using the onClick function and that will lead to the property isSubmitClicked change to true that the component NextButton will render.
The thing is that just the SubmitButton is rendered and when I click the button of the component SubmitButton it doesn't render the component NextButton.
This the part of the wrapper component where both components will be rendered if the conditions are true.
{this.state.isSelectedNumber ? <SubmitButton handleClickSumbmit={this.handleClickSumbmit}/> : null}
{this.state.isSubmitClicked ? <NextButton /> : null}
This is the part of the SubmitButton Component where the onClick function should call the handleCliclkSubmit method to change the property state:
<button type="button" onClick={this.props.handleClickSubmit}></button>
This is the handleClickSubmit method:
handleClickSumbmit () {
this.setState({
isSubmitClicked: true
});
}
I was wondering if you have any idea what could be happening and how to solve it.
You have typo error
Change
<button type="button" onClick={this.props.handleClickSubmit}></button>
To
<button type="button" onClick={this.props.handleClickSumbmit}></button>
Also the function name is not meaningful in your code. Change handleClickSumbmit to handleClickSubmit wherever you have handleClickSumbmit
Also since you are using regular function you have to bind it in constructor. I hope you already did that.
I think that you should check the calling of handleClickSubmit, where you call it as handleClickSumbmit, maybe that's where the problem lies.

Stop click on child calling method on parent - React/JavaScript

I have a modal in React. When you click the background of the modal, the modal should close. The way I have it set up right now, if you click inside* the modal, it closes as well. Because the modal is inside the background>
handleClose(e) {
e.stopPropagation();
this.props.history.push('/business/dashboard')
}
render() {
return (
<Background onClick={e => this.handleClose(e)} name="BACKGROUND">
<Container onClick={console.log("IT CLICKED")} to={'/business/dashboard'} name="CONTAINER">
....
When I click on Container, the onClick event for Background gets called. I don't want this to happen. This is a form that users will be clicking on all the time. I need the modal to only close when you click outside the modal on Background.
I think it will work if you use stopPropagation on the Container click event instead of the Background. Just make sure that you use the onClick prop in your Container component.
class App extends React.Component {
handleClose = (e) => {
e.stopPropagation();
this.props.history.push("/business/dashboard");
};
render() {
return (
<Background onClick={this.handleClose} name="BACKGROUND">
<Container
onClick={e => e.stopPropagation()}
to={"/business/dashboard"}
name="CONTAINER"
/>
</Background>
);
}
}
EDIT: On rereading the question, the other answer is a simpler solution in this case.
The behavior you want to achieve is generally referred to as an "outside click" handler. There are a couple of decent libraries to handle this [0] and their source is pretty short and readable if you want to know how it works in detail. [1]
The general idea is to register a click event handler on the document in a HOC and check whether the event.target originates inside a React ref via Element.contains browser functionality. If is is, the handler will not be executed.
[0] https://github.com/tj/react-click-outside
[1] https://github.com/tj/react-click-outside/blob/master/index.js

Categories