user9769384
user9769384

Reputation: 60

Scroll Back To Top Button Not Working in React.js

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

Answers (3)

Vijayakumar Kamaraj
Vijayakumar Kamaraj

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

Mat.Now
Mat.Now

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

J&#243;zef Podlecki
J&#243;zef Podlecki

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

Related Questions