React useState performing very badly - javascript

I am having trouble understanding the problem in this code:
export default function CustomPopup({wi,he,children}) {
//some code
const [popupSize,setPopupSize] = useState([`${wi}px`,`${he}px`])
const handlePopupSize = () =>{
let c = [];
(window.innerWidth < (wi/0.9)) ? c[0] = `90%` : c[0] = `${wi}px`;
(window.innerHeight < (he/0.8)) ? c[1] = `80%` : c[1] = `${he}px`;
if (c != popupSize) { setPopupSize(c) };
}
window.addEventListener("resize", handlePopupSize)
return (
<div className="popup--page--wrapper">
<div className="popup--box" style={{width: popupSize[0], height: popupSize[1]}}>
{ children }
</div>
</div>
)
}
When I resize the page, the page lags massively and even make the browser bug.
There seems to be something wrong with the code, but I can't find out.
Thanks in advance!

You need to add the event listener in a useEffect hook.
import React, { useState, useEffect } from 'react'
.....
.....
useEffect(() => {
window.addEventListener("resize", handlePopupSize)
return () => window.removeEventListener("resize", handlePopupSize)
},[])
Your current code creates a loop of addEventListeners, because a listener is created on every single render, and setting state is causing a new render on every resize.

Related

Window.scroll only working once (React.js)

I'm making a simple React component to remember where you are on the page and place you there when returning.
Here's the code:
function ScrollCache() {
window.scroll(0, parseInt(localStorage['scroll']));
document.addEventListener('scroll', function (e) {
if (window.scrollY != 0) {
localStorage['scroll'] = window.scrollY.toString();
}
})
return (<></>)
}
Basically, it caches the last known scroll position, and uses window.scroll(x, y) to scroll to that position. I have verified that localStorage is working as intended with a console.log immediately before the window.scroll. I've also just tried a static 100 for the y coordinate. No matter what, it only scrolls once at reload and then never again when I'm navigating around.
I'm using React Router to go between web pages. Any help is appreciated
You don't need to add the scroll event listener every time you want to cache the scroll.
Instead, try this:
const [scrollPosition, setScrollPosition] = useState(0);
useEffect(() => {
window.addEventListener("scroll", handleScroll, {
passive: true
});
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, [scrollPosition]);
useEffect(() => {
localStorage['scroll'] = scrollPosition.toString();
}, [scrollPosition);
const handleScroll = () => {
const position = window.pageYOffset;
setScrollPosition(position);
};

onDoubleClick() don't work on phone react js app

To everyone, I have a problem in my ReactJs app with the onDoubleClick() function assigned to a component, on a desktop pc my function works correctly, but on an Android mobile phone, (iPhone works fine) with the screen in vertical the function doesn't work, the screen in my phone doesn't recognize the double click as my assigned function, instead, it makes zoom in or zoom out, I want to prevent this behavior with my function.
Below is part of my code, and you can see the app in estebmaister.github.io/react-calendar/ or in the public repo with the same name.
onDoubleClick = (event) => {
this.setState({
popperAnchor: Boolean(this.popperAnchor) ? null : event.currentTarget,
});
};
render() {
return (
<td
key={"d" + this.props.day}
className={`calendar-day ${this.props.currentDay}`}
onDoubleClick={this.onDoubleClick}
>
I hope you can give me a clue to fix this problem, thanks.
this solution works for when declaring components using functions rather than classes:
....some code...
const [waitingClick, setWaitingClick] =
useState(null);
const [lastClick, setLastClick] = useState(0);
const processClick = (e) => {
if(lastClick&&e.timeStamp - lastClick < 250 &&
waitingClick){
setLastClick(0);
clearTimeout(waitingClick);
setWaitingClick(null);
console.log('double click')}
else{
setLastClick(e.timeStamp);
setWaitingClick(setTimeout(()=>{
setWaitingClick(null);
}, 251))
console.log('single click')
}
}
....some code...
<component onClick={(e)=>processClick(e)}></>
Here is my solution for double click. Please note that onDoubleClick does not work in mobile version of almost all browsers. And you need to use onTapStart and so on. But I would like to introduce a simple work around that I have tested in Chrome, FireFox and Safari and it works perfect. I made a double click via onClick event for a img tag.
render() => {
this.watingClick = null; // a reference to timeout function
this.lastClick = 0; // a watchdog for difference between 2 clicks
return ( <img
onClick={(e) => {
if (
this.lastClick &&
e.timeStamp - this.lastClick < 250 &&
this.watingClick
) {
this.lastClick = 0;
clearTimeout(this.watingClick);
console.log("Do the steps to respond double click");
this.watingClick = null;
} else {
this.lastClick = e.timeStamp;
this.watingClick = setTimeout(() => {
this.watingClick = null;
console.log("Do the steps to respond single click");
}, 251);
}
}}
/>
);
}
Here, I assumed time difference between 2 clicks is 250 mili seconds, you can increase it even to 300, however, decreasing this value in most browsers does not work.

How to use useEffect() correctly?

I want to change style by scrolling.
This code isn't working correctly
.
When I rolling up and down too many times and too fast, then the browser is going to freeze, crash.
I think I used useEffect() wrong. How can I solve this issue.
const ArticleItem = ({title, content, active, chapter, program, id, scrollPos}) => {
const ref = useRef(null);
const client = useApolloClient();
useEffect(() => {
if(ref.current.offsetTop <= (scrollPos + 200)) {
client.writeData({data: {
curChapter: chapter.num,
curArticle: id,
curProgram: program.name
}});
}
});
if(active === false)
return ( // Inactive Article
<div className='section-item' ref={ref}>
<h2>{title.toUpperCase()}</h2>
<ReactMarkdown source={content} />
<br />
</div>
)
return ( // Active Article
<div className='section-item active' ref={ref}>
<h2>{title.toUpperCase()}</h2>
<ReactMarkdown source={content} />
<br />
</div>
)
}
As a Result, I faced this warning.
Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
I think this is the reason of issue?!
Based on my comment above, you need to include the dependency array for useEffect also. In the current way it is running infinitely. Probably you want to include scrollPos into that thus it would only triggered once scrollPos is changing.
Try the following:
useEffect(() => {
if(ref.current.offsetTop <= (scrollPos + 200)) {
client.writeData({data: {
curChapter: chapter.num,
curArticle: id,
curProgram: program.name
}});
}
}, [scrollPos]);
I hope this helps!
Well, the problem is triggered all the time you can use the scroll event listener and make your changes when this event is triggered.
const [scrollItem, setScrollItem] = useState(null);
const handleScroll() {
if(scrollItem) {
// logic goes here
}
}
useEffect(() => {
window.addEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, []); // initialize event only once
return (
<div ref={setScrollItem}>
...
</div>
);
EDIT
Avoid that solution, #norbitrial is right

Javascript mouseDown - cannot read currentTarget of undefined

I have a composent with which I would allow maintaining click in order to call multiple function by push-holding. My action dispatch a simple function to Redux reducers.
The objective of my component is to allow people decrease quantity of their order by maintaining a mouse's click. So that it, to allowing visitors have a more fluent user experience.
When I trigger the function my console returns me :
Cannot read property 'currentTarget' of undefined
When I click alone one time it is great. But when I mouseDown it fails with the above message.
Here my reactComponent.js:
import React, {Component} from 'react'
import style from "./OrderRibbon.css";
import equal from 'deep-equal';
export default class OrderRibbon extends Component {
t;
start = 100;
decreaseQuantity = (e) => {
e.preventDefault();
this.props.decreaseOrder(this.props.id)
}
addOrder= (e) => {
e.preventDefault();
this.props.addOrder(this.props.id)
}
orderPushing = (e) => {
e.preventDefault();
this.orderRepeat(e);
}
orderRepeat = (e) => {
if( e.currentTarget.attributes.name.value ){
console.log("EVENT NAME IN ORDER REAPEAT: ", e.currentTarget.attributes.name.value)
}else{
console.log("EVENT NAME IN ORDER REAPEAT: ", e.target.attributes.name.value)
}
if(e.currentTarget.attributes.name.value === "addOrder"){
this.addOrder
}else{
this.decreaseQuantity
}
this.t = setTimeout(this.orderRepeat, this.start);
this.start = this.start / 2;
}
// STOP Calling function
onMouseUp = () => {
clearTimeout(this.t);
this.start = 100;
}
render(){
return (
<div className={style.order_ribbon_layout} >
<div className={`${style.title} ${style.details_element}`} >
{this.props.title}
<div className={style.quantity} >
<div className= {style.quantity_icon}></div>
<span className= {style.quantity_number} > {this.props.quantity} </span>
</div>
</div>
<div className={style.price} >
{this.props.price * this.props.quantity}
</div>
<div className={style.quantity} >
<div
onMouseUp={this.onMouseUp}
onMouseDown={this.orderPushing}
name="decreaseQuantity"
onClick={this.decreaseQuantity}
className={ `${style.cardButton}`}
id={style.decreaseQuantity}></div>
<div
onMouseUp={this.onMouseUp}
onMouseDown={this.orderPushing}
name="addOrder"
onClick={this.addOrder}
className={ `${style.addButon}`}
// ${style.details_element}
id={style.addArticle}></div>
</div>
</div>
)
}
}
I wcan't figure out what is going wrong, if any body have an hint, would be great.
You have event binding issue. You can define like this:
orderPushing = () => (e) => {
e.preventDefault();
this.orderRepeat(e);
}
Or, keeping the same as you currently have, you may use inline event binding like this:
onMouseDown={(e) => this.orderPushing(e)}

How to reveal a React component on scroll

I've created a React component for a fixed nav that I would like to remain hidden, until I scroll past a certain point on the page, then slides into view. Medium has a header similar to what I'm describing.
This is a relatively trivial task in jQuery, with scrollmagic or waypoints but is there an idiomatic way of accomplishing this with React and vanilla JS?
React Way with vanilla JS jsfiddle;
don't forget to remove EventListener. In this example component will render if only it is neccessary
class TopBar extends React.Component {
state = { isHide: false };
hideBar = () => {
const { isHide } = this.state
window.scrollY > this.prev ?
!isHide && this.setState({ isHide: true })
:
isHide && this.setState({ isHide: false });
this.prev = window.scrollY;
}
componentDidMount(){
window.addEventListener('scroll', this.hideBar);
}
componentWillUnmount(){
window.removeEventListener('scroll', this.hideBar);
}
render(){
const classHide = this.state.isHide ? 'hide' : '';
return <div className={`topbar ${classHide}`}>topbar</div>;
}
}
You could use a component such as react-headroom to do the heavy lifting for you. Or, you can still use waypoints in React, setting it up in the componentDidMount lifecycle method and removing it using componentWillUnmount.
In the componentDidMount lifecycle hook, do the same thing as in the jQuery link you have given:
class Navbar extends React.component {
let delta = 5;
render() {
return (
<div ref=header></div>
);
}
componentDidMount() {
$(window).scroll(function(event){
var st = $(this).scrollTop();
if(Math.abs(this.state.lastScrollTop - st) <= delta)
return;
if (st > lastScrollTop){
// downscroll code
// $(this.refs.header).css('visibility','hidden').hover ()
this.setState({
navbarVisible: false
});
} else {
// upscroll code
$(this.refs.header).css('visibility','visible');
this.setState({
navbarVisible: true
});
}
lastScrollTop = st;
}.bind(this));
}
}
I created a react component for this same exact need as I could not find any other implementations that matched what I needed. Even react-headroom did not give you something that would just scroll in after reaching a certain point on the page.
The gist is here: https://gist.github.com/brthornbury/27531e4616b68131e512fc622a61baba
I don't see any reason to copy the component code here. The code is largely based off of the react-headroom code but does less and is therefore simpler.
The component is the first piece of code, you could simply copy/paste then import it. After importing your code with the navbar would look something like this:
class MyScrollInNavBar extends Component {
render() {
return (
<ScrollInNav scrollInHeight={150}>
<MyNavBar />
</ScrollInNav>
);
}
}

Categories

Resources