onClick doesn't work on first click after rerender - javascript

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.

Related

React - onClick setTimeout/clear old timeout if there is one

I'm doing a toy project where I generate a lot of tints and shades of colors and clicking on them copies their hex values to the clipboard. Originally, I was using an onClick handler on the color article to set my state value alert to true (which would display "copied to clipboard") and using useEffect to set a 3 second timer with setTimeout before turning alert false so that the text would disappear after awhile.
The relevant code inside my Color component:
useEffect(() => {
const timeout = setTimeout(() => {
setAlert(false);
}, 3000);
return () => clearTimeout(timeout);
}, [alert]);
and
return (
<article
className={`color ${index > (list.length - 1) / 2 ? "color-light" : ""}`}
style={{ backgroundColor: `rgb(${rgbString})` }}
onClick={() => {
setAlert(true);
navigator.clipboard.writeText(hexValue);
}}
>
<p className="percent-value">{weight}%</p>
<p className="color-value">{rgbToHex(...rgb)}</p>
{alert && <p className="alert">copied to clipboard</p>}
</article>
);
This works, but in the case of multiple clicks on the same color article, I want to have the "copied to clipboard" alert disappear 3 seconds after the last click. In other words, I want multiple clicks to refresh the timer. Currently, it disappears 3 seconds after the first click, so subsequent clicks do not refresh the timer. This is because subsequent clicks on the color don't change the alert state value, so I can't refresh my timer in useEffect. I tried to solve this by calling setAlert(false) before setAlert(true) in my onClick to force the state to change every click and let me enter useEffect by force but this doesn't work. I've also tried different ways of removing useEffect entirely and trying to do everything in the onClick block since it's semantically closer to what I'm trying to do, but I can't figure out how to achieve my result that way. Any pointers?
What you can do is declare a variable alertTimeout outside of your component:
let alertTimeout;
function MyComponent(props) {
...
}
And in your component you define a function for the timeout triggering/refreshing :
function triggerAlert() {
if (alertTimeout) {
clearTimeout(alertTimeout);
}
setAlert(true);
alertTimeout = setTimeout(() => {
setAlert(false);
}, 3000);
}
And you call this function with onClick:
onClick={() => {
triggerAlert();
navigator.clipboard.writeText(hexValue);
}}
And you need to add a uesEffect to clear the timeout on component unmount:
useEffect(() => {
return function cleaning() {
if (alertTimeout) {
clearTimeout(alertTimeout);
}
}
}, [])

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

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;
}
...

How to close side panel on clicking anywhere in the document using react?

I want to close side panel when i click anywhere else in the document.
I have a side panel which will open on clicking a button and close on clicking on the same button. However, i want to close sidepanel even when clicking anywhere else in the document.
I have tried using document.addeventlistener('mousedown', handle_toggle) which works, but it will close the sidepanel even when I click on the sidepanel itself.
How can i fix it such that it will not close on clicking sidepanel?
Below is the code:
this.state = {
side_panel_open: false,
};
componentDidUpdate () {
if(this.state.side_panel_open) {
document.addEventListener('mousedown',this.toggle_side_panel);
}
}
handle_btn_click = () => {
this.setState({side_panel_open: !side_panel_open});
}
toggle_side_panel = () => {
this.setState({side_panel_open: false});
}
return (
<button onClick={this.handle_btn_click}>
click me
</button>
{this.state.side_panel_open &&
<Sidepanel/>
});
I followed the article from the link below and it works.
https://medium.com/#pitipatdop/little-neat-trick-to-capture-click-outside-react-component-5604830beb7f
i think this may help..
In Sidebar component use onMouseOver||onMouseLeave
Sidebar comp
closeSidebar=()=>{
this.setState({show:false})
}
return(
<div onMouseLeave={this.closeSidebar} >sidebar</div>
)
export default Sidebar

React trouble with event.stopPropagation()

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.

Categories

Resources