Reputation: 60
I am working on a React based project and try to implement a button in the footer for click to scroll top but facing an issue which I can not resolve yet. If you want to see the code please click to the below gist link.
// GoTop.js
import React from 'react';
const GoTopd = (props) => {
const [intervalId, setIntervalId] = React.useState(0);
const [thePosition, setThePosition] = React.useState(false);
React.useEffect(() => {
document.addEventListener("scroll", () => {
if (window.scrollY > 170) {
setThePosition(true)
} else {
setThePosition(false);
}
});
// window.scrollTo(0, 0);
}, [])
const onScrollStep = () => {
if (window.pageYOffset === 0){
clearInterval(intervalId);
}
window.scroll(0, window.pageYOffset - props.scrollStepInPx);
}
const scrollToTop = () => {
const intervalId = setInterval(onScrollStep, props.delayInMs);
setIntervalId(intervalId);
}
const renderGoTopIcon = () => {
return (
<div className={`go-top ${thePosition ? 'active' : ''}`} onClick={scrollToTop}>
<i className="arrow alternate circle up outline icon"></i>
</div>
)
}
return (
<React.Fragment>
{renderGoTopIcon()}
</React.Fragment>
)
}
export default GoTopd;
And import
that component on footer like below:
<GoTop scrollStepInPx="100" delayInMs="10.50" />
The issue is with this code like when the first load and go to below of that page it's working fine but after that, I can't go down to the page need to refresh again. I don't understand the issue of why it's behaviors like this.
Can anyone please figure out what is an issue?
I appreciate your help, please.
Thanks
Upvotes: 0
Views: 3226
Reputation: 21
Simple solution to scroll to top using React Hooks
ScrollTopArrow.tsx
import React, { FC, useState, useEffect } from 'react'
import {FaArrowCircleUp} from 'react-icons/fa';
import '../App.css';
type ScrollTopArrowProps = {
showBelow?: number
}
const ScrollTopArrow: FC<ScrollTopArrowProps> = ({ showBelow = 400 }) => {
const [showScroll, setShowScroll] = useState<boolean>(false)
useEffect(() => {
window.addEventListener('scroll', checkScrollTop)
return () => {
window.removeEventListener('scroll', checkScrollTop)
}
})
const checkScrollTop = () => {
if (!showScroll && window.pageYOffset > showBelow) {
setShowScroll(true)
} else if (showScroll && window.pageYOffset <= showBelow) {
setShowScroll(false)
}
}
const scrollTop = () => {
window.scrollTo({ top: 0, behavior: 'smooth' })
}
return (
<FaArrowCircleUp className="scrollTop" onClick={scrollTop} style={{height: 40, display: showScroll ? 'flex' : 'none'}}/>
)
}
export { ScrollTopArrow }
App.css
.App {
text-align: center;
height: 5000px;
}
.scrollTop {
position: fixed;
width: 100%;
bottom: 20px;
align-items: center;
height: 20px;
justify-content: center;
z-index: 1000;
cursor: pointer;
animation: fadeIn 0.3s;
transition: opacity 0.4s;
opacity: 0.5;
}
.scrollTop:hover{
opacity: 1;
}
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 0.5;
}
}
App.tsx
import React from 'react'
import './App.css'
import {ScrollTopArrow} from './components/ScrollTopArrow'
function App() {
return (
<div className="App">
<ScrollTopArrow/>
</div>
);
}
export default App
Upvotes: 2
Reputation: 1725
Using React you can do very simple solution to scroll to top, but I show how improve your code. You can use refs.
Note that inrevaltId
in clearInterval is 0, not reference to the interval, so you can't clear interval!
const timeoutRef = React.useRef(null);
const onScrollStep = () => {
if (window.pageYOffset === 0){
clearInterval(timeoutRef.current);
}
window.scroll(0, window.pageYOffset - props.scrollStepInPx);
}
const scrollToTop = () => {
timeoutRef.current = setInterval(onScrollStep, props.delayInMs);
}
Fiddle example: https://jsfiddle.net/4ef2boxm/
Upvotes: 1
Reputation: 11283
Here's an alternative to setInterval
const {
useRef,
useState,
useEffect
} = React;
const App = () => {
const ref = useRef(null);
const [isBottom, setBottom] = useState(false);
useEffect(() => {
}, [])
useEffect(() => {
const element = ref && ref.current;
if (element) {
const checkIsBottomOfPage = () => {
if (window.scrollY >= window.innerHeight) {
setBottom(true)
} else {
setBottom(false);
}
}
document.addEventListener("scroll", checkIsBottomOfPage);
window.scrollTo({
top: element.scrollHeight
})
checkIsBottomOfPage();
return () => {
return document.removeEventListener("scroll", checkIsBottomOfPage);
}
}
}, [ref])
const onScroll = (top) => {
const incOrDec = top ? 1 : -1;
let chunk = -1;
let position = window.scrollY;
let isScrollComplete = null;
if(top === 0) {
chunk = -ref.current.scrollHeight / 3;
isScrollComplete = () => window.scrollY < 10;
}
else {
chunk = top / 3;
isScrollComplete = () => window.scrollY >= window.innerHeight
}
const scrollStep = () => {
position = position + chunk;
window.scrollTo({
behavior: 'smooth',
top: position
})
if(isScrollComplete()) {
return;
}
setTimeout(scrollStep, 300);
}
setTimeout(scrollStep, 300)
}
const onScrollBottom = () => onScroll(ref.current.scrollHeight);
const onScrollTop = () => onScroll(0);
return <div ref = {
ref
} >
<
button style = {
{
padding: '.5rem'
}
}
onClick = {
onScrollBottom
} > Scroll To Bottom < /button> <
div style = {
{
background: 'gray',
height: '200vh'
}
} > < /div> <
button style = {
{
visibility: isBottom ? 'visible' : 'hidden',
padding: '.5rem'
}
}
onClick = {
onScrollTop
} > Scroll To Top < /button> <
/div>
}
ReactDOM.render( <
App / > ,
document.getElementById('root')
);
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<div id="root"></div>
Upvotes: 0