React trouble with event.stopPropagation() - javascript

I have two components here, the first one is a table, and I have an on-click event attached to one of the <td>'s in every row that summons a little tooltip-like window:
<td onClick={ () => loadSelectorWindow(p.product_id) }>
{
p.selectorActive &&
<SelectorWindow
cancelWindow={this.cancelSelectorWindow}
product_id={p.product_id}/>
}
</td>
The function bound to the <td> click will search through all products in state and flip a boolean on the selected product to display the tooltip.
loadSelectorWindow = (product_id) => {
this.setState({ products: this.state.products.map( p => {
if (p.product_id == product_id) {
p.variationSelectorActive = true
} else {
p.variationSelectorActive = false
}
return p
})})
}
However, the tooltip also needs a button with a window cancel event linked to it:
// within <SelectorWindow />
<p onClick={ () => {cancelWindow(event)} }> X </p>
This function cycles through state and sets all of the display booleans to false.
cancelSelectorWindow = (event) => {
event.stopPropagation()
this.setState ({ products: this.state.products.map( p => {
p.variationSelectorActive = false
return p
})})
}
Putting breakpoints in the code I can see that the cancel button is correctly calling the cancel function and setting the displayTooltip boolean to false, temporarily. The problem is, the loadSelectorWindow is ALSO getting fired when the cancelWindow button is clicked, and the boolean is set back to true DX.
This is why I attempted to put the event.stopPropagation call in there but obviously something is still calling it. There is no other place in my code that the loadSelectorWindow function is mentioned... Any ideas how I can stop it from getting called?

I forgot to pass event to the cancelWindow callback function. React why is your syntax so confusing sometimes...
Fix:
<p onClick={ (event) => {cancelWindow(event)} }> X </p>

You have one html element nested inside the other, so if you click the inner one then you will receive onClick events for both. So that is what you are getting. You need to redesign the layout of the page so that does not happen.

Related

onClick doesn't work on first click after rerender

const NavigationButtons = ({onBtnClicked, btnClass, label, route, btnAct}) => {
return (
<p
className={`btn ${btnClass} ${btnAct}`}
onClick={() => onBtnClicked(route)}>
{label}
</p>
);
};
This is my button component, I'm giving it to another component as btns1 props
{!isSigned?btns1:windowPixel?btns1:null}
Basically, when isSigned is false, btns1 is rendered. There's really no problem here. When isSigned is true, it checks if windowPixel is true, this is changed to true by App.js as a state by measuring the current window. It works perfectly unless I click the button. Resize the window where windowPixel will be false, then on my first click, it doesn't trigger onClick. After that onClick works again.
componentDidMount() {
if (window.matchMedia(`(max-width: 990px)`).matches) {
this.resizeWindow(true);
}
window.addEventListener("resize", () => this.resizeWindow(window.matchMedia(`(max-width: 990px)`).matches));
}
This is what checks the window size for windowPixel. chatScroll, is for the panel that expands when btn1 is clicked, btnAct is just for a css that change the color of the button while the panel is expanded. For now, I've put click(), like a bandaid.
resizeWindow = (windowPixel) => {
const {chatScroll, btn1Act} = initialState;
if (windowPixel !== this.state.windowPixel) {
if (windowPixel === false) {
if (this.state.isSigned) {
document.getElementById('btn1').click();
this.setState({chatScroll, btn1Act});
}
}
this.setState({windowPixel});
}
};
The Reason Is Simple Brother in First Click Your Object or function or variable what ever it is , Just Initialize in first click and when you click second time it will Work as per your code.

Manipulating dom elements dynamically in react?

I am trying to have a button change color when the user clicks it in React. Each button I am trying to add this functionality to is also updating state. How can I interact with styles AND update state when these buttons are clicked? Here's my crude attempt:
Some HTML:
<div className="searchPrivacyContainer">
<p>USERS CAN SEARCH FOR MY PROJECT:</p>
<div className="searchPrivacySelect">
<div
className="searchPrivacyYes"
onClick={
(holdColor,
function () {
setProposal({ ...proposal, searchable: true });
})
}
>
And this is the most basic version of the function I'm trying to bind. Writing the body of the function should be the straightforward part hopefully, I just don't know how to bind it to the div which is already calling another function:
const holdColor = (event) => {
console.log(event.target.style);
};
I think this part isn't not working as you expecting,
onClick = {
(holdColor,
function() {
setProposal({ ...proposal,
searchable: true
});
})
}
Simple test,
function x(ev){console.log('x', ev)}
function y(ev){console.log('y', ev)}
const listener = (x, y)
listener(123)
Look, first one (x) never got called, So, you might wanna do something like this on your listener.
onClick={(ev) => {
holdColor(ev)
setProposal({ ...proposal,
searchable: true
});
}}
EDIT:
see this working example (it might be helpful), https://codesandbox.io/s/eloquent-wilson-drbbk?fontsize=14&hidenavigation=1&theme=dark

Using event listeners with React.useEffect

Code Sandbox here
Hi, I want to create a container so that if you click the button inside of the container, a box appears. I have added an event listener so that if you mouse out of the container, the box will no longer show. Of course, I need to also add an event listener to the button inside of the container so that hovering over the button won't hide the box if you have the box already visible.
However, I am having difficulty with this and am seeing some side effects. For example, if you as click the button to show the box, and then click it again to hide the box, then hovering over the button will show the box again, which is not the desired affect.
I believe there is something I am doing wrong using React.useEffect and not tracking state properly, but I am unsure.
When writing a useEffect that manually adds some kind of event listener, 99% of the time you must have it return a cleanup function that will remove said event listener. Otherwise you end up with dangling event listeners that haven't been cleaned up between useEffect calls.
React.useEffect(() => {
const handleButtonMouseOver = (): void => {
if (show) {
setShow(true);
}
};
// const handleMouseOut = (): void => {
// if (show) {
// setShow(false);
// }
// };
// Save reference to this element in the scope of the function
// buttonRef.current may change between triggers
const buttonElement = buttonRef.current;
if (buttonElement) {
buttonElement.addEventListener("mouseover", handleButtonMouseOver);
}
// if (containerRef.current) {
// containerRef.current.addEventListener("mouseout", handleMouseOut);
// }
// Add this
return () => {
buttonElement.removeEventListener("mouseover", handleButtonMouseOver);
};
}, [buttonRef, containerRef, show]);
https://reactjs.org/docs/hooks-effect.html#effects-with-cleanup

How to access more than one button from 1 onClick function in React?

I've made a function, which will be called when a button is clicked. I want to use it for two buttons, but after implementing it, when I click on one button, both get clicked at the same time?
This is the function, which will be called after onClick -
showMore = () => {
this.setState((prevState) => {
return { showMore: !prevState.showMore };
});
};
This is the condition I've used to call those two buttons (I know, I can do something here to make it just two if conditions rather than 4, suggestions?)
if (!this.state.showMore) {
accounts = take(accounts['demo_accounts'], DEFAULT_NO_OF_ACCOUNTS);
}
if (this.state.showMore) {
accounts = accountsData['demo_accounts'];
}
if (!this.state.showMore) {
cardNumbers =
take(cardNumbers['demo_numbers'], DEFAULT_NO_OF_ACCOUNTS);
}
if (this.state.showMore) {
cardNumbers = accountsData['demo_numbers'];
}
And I am using it for 2 buttons, however, both the buttons are getting clicked at the same time, I know I can use an ID for button, but that doesn't seen to work as well.
To sum it up, two questions basically -
How to reduce the 'if' statements?
How to stop both the buttons getting clicked?
Similar answer, but you can set it so that each button has a different ID and attaches to a different event handler, like so:
<button id="btn1">Click me!</button>
<button id="btn2">Me too!</button>
Javascript:
const button1 = document.getElementById("btn1").addEventHandler('click', function);
const button2 = document.getElementById("btn2").addEventHandler('click', function2);
How to reduce the 'if' statements?
Just combine the like conditions. Since all your conditions are basically checking the value of this.state.showMore you really only need 1 single conditional test.
if (this.state.showMore) {
accounts = accountsData['demo_accounts'];
cardNumbers = accountsData['demo_numbers'];
} else {
accounts = take(accounts['demo_accounts'], DEFAULT_NO_OF_ACCOUNTS);
cardNumbers =
take(cardNumbers['demo_numbers'], DEFAULT_NO_OF_ACCOUNTS);
}
How to stop both the buttons getting clicked?
When I want to use a single general click handler across multiple buttons I use a Higher Order Function, which is a function that returns a function. So in this case I create a clickHandler = id => () => this.setState({ [id]: <value> });
This callback takes an id parameter that is used when attaching the callback to the onClick handler of an element, and it is invoked right away and returns a new function that is actually used as the callback with the id saved in the enclosure.
<button onClick={this.clickHandler('a')>Button A</button>
<button onClick={this.clickHandler('b')>Button B</button>
Here is a codesandbox example of a HOF handler

React Hooks: useEffect for modal event listener

I have a modal dialog that I want to close if the user clicks outside of the modal. I have written the following useEffect code but I run into following issue:
The modal dialog contains a number of children (React Nodes) and those children might change (e.g. the user deletes an entry of a list). Those interactions trigger my onClick method but as the clicked list item has been removed from the modal, the modal closes even though the click was within the modal.
I thought adding [ children ] at the second parameter for useEffect would cleanup the old effect event listener quick enough that the method does not run again but this is not the case.
I handled the same issue in a class component with a ignoreNextClick-state but there must be a cleaner solution, right?
useEffect( () => {
const onClick = ( event ) => {
const menu = document.getElementById( 'singleton-modal' );
if ( !menu ) return;
// do not close menu if user clicked inside
const targetInMenu = menu.contains( event.target );
const targetIsMenu = menu === event.target;
if ( targetInMenu || targetIsMenu ) return;
onCloseModal();
};
window.addEventListener( 'click', onClick, false );
return () => window.removeEventListener( 'click', onClick, false );
}, [ children ] );
I found a solution that does not require any sort of storing old props.
The useEffect call looks like this:
useEffect( () => {
const onClickOutside = () => onCloseModal();
window.addEventListener( 'click', onClickOutside, false );
return () => window.removeEventListener( 'click', onClickOutside );
}, [] );
Adding the following click listener to the modal directly will stop the window click-listener from being called if the user clicked inside the modal.
<div
className={`modal ${ classes }`}
onClick={event => event.stopPropagation()}
role="presentation"
>
{children}
</div>`
I also added the role presentation to make the modal more accessible and aria-conform.
You can check parent of modal from the event.target.
If the current target is within the modal then return.
You can use closest to do that.
See the following solution.
...
if (event.target.closest( '.singleton-modal' ) || event.target.classList.contains('singleton-modal')) {
return;
}
...

Categories

Resources