How to use useEffect() correctly? - javascript

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

Related

Function does not work when I go to the page back, and then return to the page (React)

function Final_Set(){
return(
<div id="exercise_sets_decider_box">
{
(function(){
console.log(1);
Final_set_page();
})()
}
</div>
);
function App(){
const [mode, Setmode] = useState("0");
if (mode === "0"){
$(document).on('click', "#purple_box_1", function(e){
Setmode("Final");
});
return(
<div id="purple_box_1">
</div>
);
} else if(mode==="Final"){
$(document).on('click', "#purple_box_2", function(e){
Setmode("0");
});
return(
<div>
<Final_Set></Final_Set>
<div id="practice"></div>
<div id="purple_box_2">
</div>
</div>
)
}
}
In brief, Final_set_page function is appending html code in div:#practice, like $("#practice").append('blahblah'). If I go from mode '0' to mode 'Final', Final_set_page() is well working. But if I return to mode '0' and go back to mode 'Final', console.log() is well working but Final_set_page() is not working. I don't know the reason, and I can't find the solution. Please help!
Maybe the problem is in jQuery.
I have rewritten the code. Maybe edit this code instead according to your needs.
By the way, we have no idea what Final_set_page is...
function Final_Set() {
useEffect(() => {
console.log(1);
Final_set_page(); // I don't know what it is, so leaving it here. Move it if it is a component.
}, []); // [] empty dependency array means useEffect only runs once.
return (
<div id="exercise_sets_decider_box">
exercise_sets_decider_box
</div>
);
}
function App() {
const [mode, setMode] = useState("0"); // SetMode does not follow the convention for naming variables.
const handlePurpleBox1Click = () => setMode("Final"); // I moved this to separate functions so it is easier for you to edit them further.
const handlePurpleBox2Click = () => setMode("0");
if (mode === "0") {
return (
<div id="purple_box_1" onClick={handlePurpleBox1Click}>
purple_box_1
</div>
);
}
if (mode === "Final") { // if the previous return worked, this code is not reachable. This looks cleaner to me.
return (
<div>
<Final_Set></Final_Set>
<div id="practice"></div>
<div id="purple_box_2" onClick={handlePurpleBox2Click}>
purple_box_2
</div>
</div>
)
}
return <></> // add this, so the component returns something just in case.
}

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

React useState performing very badly

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.

UseEffect won't use updated state

I have a react function which is supposed to be my header. This header shall change its background color after reaching a button on scroll.
To do so I use a scroll event listener and track its position in relation to the button. This works fine for setTransparent(false), but not for setTransparent(true): Logging transparent inside of the listener returns true even after setting it to false inside of the first if-statement.
How so? What's the best practice here?
const [transparent, setTransparent] = useState(true);
useEffect(() => {
const button = document.querySelector(".hero-button");
window.addEventListener("scroll", () => {
const {bottom} = button.getBoundingClientRect();
if (transparent && bottom <= 0) {
setTransparent(false);
} else if (!transparent && bottom > 0) {
setTransparent(true);
}
});
}, [])
Setting the dependency to transparent will make it work, but this will add even listener every time it updates.
Your transparent variable in the effect callback only references the value on the initial render, which is always true. You can fix it by re-adding the scroll listener whenever transparent changes, and return a cleanup function that removes the prior handler:
useEffect(() => {
const button = document.querySelector(".hero-button");
const scrollHandler = () => {
const { bottom } = button.getBoundingClientRect();
if (transparent && bottom <= 0) {
setTransparent(false);
} else if (!transparent && bottom > 0) {
setTransparent(true);
}
};
window.addEventListener("scroll", scrollHandler);
// DON'T FORGET THE NEXT LINE
return () => window.removeEventListener("scroll", scrollHandler);
}, [transparent]);
Another option would be to use a ref instead of useState for transparent (or, in addition to useState if transparent changes needs to result in re-rendering).

Auto-scroll to bottom of the messages

I am working on a chat app, everytime I put a message I need to scroll to the bottom in order to see the new messages. So, as in a regular chat, I need to provide the functionality that the user may be able to see the last messages without manually scrolling to the bottom.
I am using React, is there a css way? or can you tell me the best way to do that ?
let me show you some code
this is the main component
render () {
let messages = this.props.chatMessages.map((message) => {
return <ChatItem info={message.info} me={message.me} player={message.player} message={message.message} />;
});
let chatForm;
if (this.props.mode === 'player') {
chatForm = <ChatForm onAddMessage={this.addMessage} />;
}
return <div className="Chat">
<ul className="Chat__messages">{messages}</ul>
<hr />
<div>{chatForm}</div>
</div>;
}
here are the ChatItem and ChatForm components
render () {
let item;
const { message, player, me, info } = this.props;
if (info) {
item = <li><em>{message}</em></li>;
}
else if (me) {
item = <li><strong>Me: {message}</strong></li>;
}
else {
item = <li><strong>{player}:</strong> {message}</li>;
}
return item;
}
render () {
return <div className="ChatForm">
<input
className="ChatForm__input"
placeholder="Your message..."
ref="newMessage"
onKeyDown={this.onKeyDown}
autofocus="true" />
</div>;
}
ADDING INFO
I need something like this http://codepen.io/TimPietrusky/pen/ylkFs
Look at this part
// Scroll the latest line of output
output.scrollTop(
output[0].scrollHeight - output.height()
);
how should I adapt it to my code on React ?
One way to do it is to compare current and previous/next props in a lifecycle event such as componentDidUpdate and scroll to bottom if a new message was added. For example:
componentDidUpdate(prevProps) {
// Check if new message was added, for example:
if (this.props.messages.length === prevProps.messages.length + 1) {
// Scroll to bottom
}
}

Categories

Resources