React's stopPropagation does not work for parent event handlers - reactjs

I tried solutions provided on the internet but no luck. Here is the codepen link. This is how the code looks like:
//JSX:
<div className="drawer-component" onClick={this.closeDrawer}>
<div className="drawer-opener">
<button onClick={this.toggleDrawer}>Toggle</button>
</div>
{this.state.isOpen && <div class="drawer">Hello I am a drawer. Cliking on me should not close myself.</div>}
</div>
// Event Handlers:
toggleDrawer(e) {
e.stopPropagation();
this.setState((state, props) => {
return {isOpen: !state.isOpen};
})
}
closeDrawer(e) {
e.stopPropagation();
this.setState((state, props) => {
return {isOpen: false};
})
}
No idea what I am doing wrong, any help would be appreciated.

The behaviour matches the code written.
You have your text in a div(class='drawer') without any click handler, so the click handler of the parent (div class='drawer-component') gets executed, and in this case it is setting your state variable isOpen to false and closing your drawer. The e.stopPropagation() in this does not affect anything since that div does not have any further parent with a click handler to which the event might get propagated.
If what you intended was to have your text clickable without closing your drawer, add a click handler for the div (with class='drawer') which just prevents propagation.
closeDrawer(e) {
// e.stopPropagation(); This isn't required as there's nowhere further the event can get propagated
this.setState((state, props) => {
return {isOpen: false};
})
}
// Prevent click handler of parent being called and closing the drawer
preventClosingOnTextClick(e) {
e.stopPropagation();
}
render() {
return (
<div className="drawer-component" onClick={this.closeDrawer}>
<div className="drawer-opener">
<button onClick={this.toggleDrawer}>Toggle</button>
</div>
{
this.state.isOpen &&
<div class="drawer"
onClick={this.preventClosingOnTextClick}> // This prevents the closing
Hello I am a drawer. Cliking on me should not close myself.
</div>
}
</div>
);
}
};
);

Related

React onClick event firing for child element

Click Handler.
const handleClick = (event) => {
console.log(event.target);
}
Return.
{ menu.map((menuItem, index) => (
<div onClick={handleClick}>
<div>{menuItem.text}</div>
<img src={menuItem.image} /></div>
))
}
My console tells me that the event.target is the IMG element instead of the DIV. Why is this happening and how can I lock the event to the parent DIV? Do I just have to write logic to look at the IMG parent element?
Thanks y'all.
this is because Bubbling and capturing. event.target is the “target” element that initiated the event. it's the event that you clicked on it and in this case, it was an image. I think you can get div by event.currentTarget because the handler runs on it.
Not sure about your code, but forcing the scope to the one of the div should work as well:
const handleClick = (event) => {
console.log(event.target);
}
{
menu.map((menuItem, index) => (
<div onClick={evt => handleClick(evt)}>
<div>{menuItem.text}</div>
<img src={menuItem.image} /></div>
))
}
Which is basically what you had, but I edited <div onClick={evt => handleClick(evt)}>.

How to remove full page reload

I have an a link in the return section of a react component and when
I click on the link the whole page reloads. I don't want the whole page
to reload. How can I correct this?
const Tester = () => {
...
...
...
if (showComponent) {
return (
<div className={classes.root}>
<div className={classes.messageWrap}>
<a
href=''
onClick={() => toggle()}
className={classes.messageText}
>
{userType}
</a>
</div>
</div>
)
}
return null
}
Your click handler will receive an event argument. You can prevent the event from propagating up and/or prevent the default behavior (in this case, navigating) by calling event.stopPropagation() and event.preventDefault() respectively.
const handleClick = e => {
e.preventDefault();
// do other stuff.
}
<a href='' onClick={handleClick}>...</a>
I would suggest, however, that a <button> makes more sense semantically if you're not using it to navigate.
const handleClick = () => {
// do stuff. no need to fight the default browser behavior.
}
<button onClick={handleClick}>...</button>

"Error: Too many re-renders. React limits the number of renders to prevent an infinite loop."

Hi I have been stuck in a React Function useState. I just want to learn hooks and useState, but I can not have any progress even struggling too much to find a solution. Here is my full react function:
import React, { useState } from 'react';
import './MainPart.css';
function MainPart(props) {
const [orderData_, setOrderData_] = useState(props.orderData);
let topicData_ = props.topicData;
let titleData_ = props.titleData;
let infoData_ = props.infoData;
return (
<div className='MainPart'>
<div className='mainWindow'>{getPics(orderData_)}</div>
<div className='information'>
<div className='moreNewsDivs'>
<div className='moreNewsDiv1'>
<h4>MORE NEWS</h4>
</div>
<div className='moreNewsDiv2'>
<button
className='previous-round'
onClick={setOrderData_(previous(orderData_))}
>
‹
</button>
<button href='/#' className='next-round'>
›
</button>
</div>
</div>
<hr />
<div className='topicDiv'>
<h5 className='topicData'>{topicData_}</h5>
<h5 className='titleData'>{titleData_}</h5>
<h6 className='infoData'>{infoData_}</h6>
</div>
</div>
</div>
);
}
function previous(orderData_) {
let newOrderData;
if (orderData_ === 3) {
newOrderData = 2;
console.log(newOrderData);
return newOrderData;
} else if (orderData_ === 1) {
newOrderData = 3;
console.log(newOrderData);
return newOrderData;
} else {
newOrderData = 1;
console.log(newOrderData);
return newOrderData;
}
}
function next(orderData_) {
let newOrderData;
if (orderData_ === 3) {
newOrderData = 1;
} else if (orderData_ === 2) {
newOrderData = 3;
} else {
newOrderData = 2;
}
return newOrderData;
}
const getPics = picOrder => {
if (picOrder === 1) {
return (
<img
src={require('../assets/desktopLarge/mainImage.png')}
className='MainImage'
alt=''
id='mainImage'
/>
);
} else if (picOrder === 2) {
return (
<img
src={require('../assets/desktopLarge/bridge.png')}
className='MainImage'
alt=''
id='mainImage'
/>
);
} else {
return (
<img
src={require('../assets/desktopLarge/forest.png')}
className='MainImage'
alt=''
id='mainImage'
/>
);
}
};
export default MainPart;
I am getting an error while using useState. Even loading the page fresh and not pressed to anything my buttons onClick event listener activated and As I mentioned before at the topic My Error:
"Error: Too many re-renders. React limits the number of renders to
prevent an infinite loop."
The problem can be found in your onClick prop:
<button className="previous-round" onClick={setOrderData_(previous(orderData_))}>‹</button>
^
Everything between the curly braces gets evaluated immediately. This causes the setOrderData_ function to be called in every render loop.
By wrapping the function with an arrow function, the evaluated code will result in a function that can be called whenever the user clicks on the button.
<button className="previous-round" onClick={() => setOrderData_(previous(orderData_))}
>‹</button>
You can find more information about JSX and expressions in the official docs
https://reactjs.org/docs/introducing-jsx.html#embedding-expressions-in-jsx
Infinite re-render loop
The reason for the infinite loop is because something (most likely setState) in the event callback is triggering a re-render. This will call the event callback again and causes React to stop and throw the 'Too many re-renders.' error.
Technical explanation
To better understand the reason why JSX works this way see the code below. JSX is actually being compiled to Javascript and every prop will be passed to a function in an Object. With this knowledge, you will see that handleEvent() is being called immediately in the last example.
// Simple example
// JSX: <button>click me</button>
// JS: createElement('button', { children: 'click me' })
createElement("button", { children: "click me" });
// Correct event callback
// JSX: <button onClick={handleClick}>click me</button>
// JS: createElement('button', { onClick: handleClick, children: 'click me' })
createElement("button", { onClick: handleClick, children: "click me" });
// Wrong event callback
// JSX: <button onClick={handleClick()}>click me</button>
// JS: createElement('button', { onClick: handleClick(), children: 'click me' })
createElement("button", { onClick: handleClick(), children: "click me" });
Just replace your button with the one below
<button className="previous-round" onClick={() => setOrderData_(previous(orderData_))}>‹</button>
This happens because onClick function if used without an anonymous functions gets called immediately and that setOrderData again re renders it causing an infinite loop. So its better to use anonymous functions.
Hope it helps. feel free for doubts.
When I go through your code, I found something.
Onclick function needs to be arrow function. Onclick is an event and you are just calling a function inside onclick directly. This leads to too many re-renders because you are setting state directly inside the return. That does not work.
Calling setState here makes your component a contender for producing infinite loops. render should remain pure and be used to conditionally switch between JSX fragments/child components based on state or props. Callbacks in render can be used to update state and then re-render based on the change
This above line taken from link here: https://itnext.io/react-setstate-usage-and-gotchas-ac10b4e03d60
Just use Arrow (=>) function:
<button className="previous-round" onClick={() => setOrderData_(previous(orderData_))}>
‹
</button>
use
<button
className='previous-round'
onClick={() => setOrderData_(previous(orderData_))}
>
‹
</button>
This worked for me...

How to pass a callback to a child without triggering it

I have a React app with modal, that pop-ups with rules of the game when one clicks a button. What I want to do is make it so when I click anywhere outside this pop up window it will close. i have three files. app.js, dialog.js, and outsidealerter.js . In my main app.js when I click a button it sets a state to visible, so my element takes it and renders based upon it. my outsideralerer.js basicly detects if there is a click outside anything wrapped with specific tags. Now the problem comes that i have a method that changes the state of visibility in app.js, so in order for outsderalerter.js to use it, I pass it to it so it can have access to my main state and change it so that when a click is outside the zone the pop up window disappears. Kind of works except it closes it down even if i click within a pop up window, because when i pass the value to outsidealerter it considers the whole body as a no click zone. My question is how can I prevent it from triggering and just pass it a value, or is it possible to change the state value of app.js from outsidealerter.js
App.js
updateState() {
this.setState({ isOpen: false });
}
<div id='rule-button'>
<button onClick={(e)=>this.setState({isOpen : true})} id="modalBtn" class="button">Open Rules</button>
</div>
<OutsideAlerter updateParent={ this.updateState.bind(this)}/>
<Dialog isOpen={this.state.isOpen} onClose={(e)=>this.setState({isOpen : false})}>
</Dialog>
outsidealerter.js
handleClickOutside(event) {
if (this.wrapperRef && !this.wrapperRef.contains(event.target)) {
//alert('You clicked outside of me!');
{this.props.updateParent()};
}
}
I think it will be simpler to have the modal take the full space of the window height and width and just make it invisible except for the content of what you want to show.
We can wrap the modal with onClick={hideModal} and wrap the inner content with onClick={e => e.stopPropagation()} which will prevent our wrapper for triggering the hideModal handler.
class ModalWrapper extends React.Component {
state = { isModalOpen: true };
toggleModal = () => {
this.setState(({ isModalOpen }) => ({
isModalOpen: !isModalOpen
}));
};
render() {
const { isModalOpen } = this.state;
return (
<div className="App">
<button onClick={this.toggleModal}>Open Modal</button>
{isModalOpen && <Modal hideModal={this.toggleModal} />}
</div>
);
}
}
function Modal({ hideModal }) {
return (
<div onClick={hideModal} className="modal">
<div onClick={e => e.stopPropagation()} className="modal__content">
Modal content
</div>
</div>
);
}
Working example

React - Setting state to target with onClick method

I am trying to recreate a tabs component in React that someone gave me and I am getting stuck while getting the onClick method to identify the target.
These are the snippets of my code that I believe are relevant to the problem.
If I hardcode setState within the method, it sets it appropriately, so the onClick method is running, I am just unsure of how to set the tab I am clicking to be the thing I set the state to.
On my App page:
changeSelected = (event) => {
// event.preventDefault();
this.setState({
selected: event.target.value
})
console.log(event.target.value)
};
<Tabs tabs={this.state.tabs} selectedTab={this.state.selected}
selectTabHandler={() => this.changeSelected}/>
On my Tabs page:
{props.tabs.map(tab => {
return <Tab selectTabHandler={() => props.selectTabHandler()} selectedTab={props.selectedTab} tab={tab} />
})}
On my Tab page:
return (
<div
className={'tab active-tab'}
onClick={props.selectTabHandler(props.tab)}
>
{props.tab}
</div>
When I console.log(props.tab) or console.log(event.target.value) I am receiving "undefined"
There are a few issues causing this to happen. The first issue is that you wouldn't use event.target.value in the Content component because you aren't reacting to DOM click event directly from an onClick handler as you are in Tab, instead you are handling an event from child component. Also keep in mind that event.target.value would only be applicable to input or similar HTML elements that have a value property. An element such as <div> or a <span> would not have a value property.
The next issues are that you aren't passing the tab value from Tabs to Content and then from within Content to it's changeSelected() handler for selectTabHandler events.
In addition the onClick syntax in Tab, onClick={props.selectTabHandler(props.tab)} is not valid, you will not be able to execute the handler coming from props and pass the props.tab value. You could instead try something like onClick={() => props.selectTabHandler(props.tab)}.
Content - need to pass tab value coming from child to changeSelected():
render() {
return (
<div className="content-container">
<Tabs
tabs={this.state.tabs}
selectedTab={this.state.selected}
selectTabHandler={tab => this.changeSelected(tab)}
/>
<Cards cards={this.filterCards()} />
</div>
);
}
Tabs - need to pass tab coming from child to selectTabHandler():
const Tabs = props => {
return (
<div className="tabs">
<div className="topics">
<span className="title">TRENDING TOPICS:</span>
{props.tabs.map(tab => {
return (
<Tab
selectTabHandler={tab => props.selectTabHandler(tab)}
selectedTab={props.selectedTab}
tab={tab}
/>
);
})}
</div>
</div>
);
};
export default Tabs;
Also don't forget the unique key property when rendering an array/list of items:
<Tab
key={tab}
selectTabHandler={tab => props.selectTabHandler(tab)}
selectedTab={props.selectedTab}
tab={tab}
/>
Here is a forked CodeSandbox demonstrating the functionality.

Resources