๊ฐœ๋ฐœ ๐Ÿพ/ReactJS

React Hook ์—์„œ Scroll ์ปจํŠธ๋กค ํ•˜๊ธฐ! top๋ฒ„ํŠผ ๊ตฌํ˜„

JOTOKKI 2021. 2. 9. 23:10
728x90

 

How can I make a Top Button, click to scroll to the top ?!

 

 

์„œ๋น„์Šค์šฉ ํ™ˆํŽ˜์ด์ง€๋ฅผ ๊ฐœ๋ฐœํ•˜๋‹ค ๋ณด๋ฉด ํ•ญ์ƒ ๊ตฌํ˜„ํ•ด์•ผ ํ•˜๋Š” ๋ถ€๋ถ„์€ ์Šคํฌ๋กค ์ž…๋‹ˆ๋‹ค. 

์ฒ˜์Œ์—๋Š” ๋ฌด์กฐ๊ฑด ref ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ปจํŠธ๋Ÿด ํ•ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์—ˆ๋Š”๋ฐ, ์–ด์จŒ๋“  ๋ฆฌ์•กํŠธ๋„ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋‹ค ๋ณด๋‹ˆ ๊ผญ ๊ทธ๋ ‡์ง€๋งŒ์€ ์•Š๋”๋ผ๊ตฌ์š”. useEffect์—์„œ addEventListener๋ฅผ ๊ฑธ์–ด์ฃผ๊ณ  removeEventListener๋กœ clean-up์„ ํ•ด์ฃผ๊ธฐ๋งŒ ํ•˜๋ฉด ๋˜๋”๋ผ๊ตฌ์š”. ์ƒ๊ฐ๋ณด๋‹ค ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌํ˜„ ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. 

 

๋‹ค์Œ๊ณผ ๊ฐ™์ด ํด๋ฆญํ•˜๋ฉด ํŽ˜์ด์ง€ ์ตœ์ƒ๋‹จ์œผ๋กœ ์Šคํฌ๋กค ๋˜๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด ๋ณด๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค. 

 

์šฐ์„  ๊ตฌ์กฐ๋ฅผ ์žก์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. 

<div className="wrap">
  <button className="topBtn">TOP</button>
  <div className="inner">
    <h1>Fixed Scroll Top</h1>
      Lorem ipsum dolor sit amet consectetur adipisicing elit. Mollitia, voluptas ratione libero earum alias, officia temporibus magni possimus atque rem distinctio autem unde eaque. Minus quaerat odio mollitia asperiores neque?
      ...
  </div>
</div>

 

CSS๋„ ๋ฏธ๋ฆฌ ์˜ฌ๋ ค๋‘๊ฒ ์Šต๋‹ˆ๋‹ค. ๋””์ž์ธ์„ ์กฐ๊ธˆ์ด๋ผ๋„ ์ด์˜๊ฒŒ ํ•˜๊ณ ์ž ๋…ธ์˜ค๋ ฅ ํ–ˆ์ง€๋งŒ.. ๐Ÿ˜…

CSS๋Š” ๋”ฐ๋กœ ์„ค๋ช… ๋“œ๋ฆฌ์ง€ ์•Š๊ฒ ์Šต๋‹ˆ๋‹ค. 

.wrap {
  position: relative;
  padding: 30px;
  font-size: 18px;
  line-height: 1.6;
  background: lightgray;
}

.topBtn {
  position: fixed; 
  opacity: 0; 
  bottom: 40px; 
  right: 40px;
  z-index: -10; 
  width: 50px; 
  height: 50px;
  border-radius: 100%;
  border: 0 none;
  background: lightpink;
  color: blueviolet;
  border: 2px solid blueviolet;
  font-size: 18px;
  font-weight: bold;
  letter-spacing: -0.06em;
  box-shadow: 1px 1px 6px 3px rgba(0,0,0,0.3);
  cursor: pointer;
  transition: opacity 0.3s ease-in;
}

.topBtn.active {
  z-index: 10; 
  opacity: 1; 
}

.topBtn:hover,
.topBtn:focus,
.topBtn:active { 
  outline: 0 none; 
}

 

 

์ž, ๊ทธ๋Ÿผ ๊ตฌํ˜„ํ•˜๊ณ  ์‹ถ์€ ๋‚ด์šฉ์„ ์ •๋ฆฌํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค! 

 

1. ์Šคํฌ๋กค์„ 100px ์ด์ƒ ๋‚ด๋ฆฌ๋ฉด TOP ๋ฒ„ํŠผ์ด ๋‚˜ํƒ€๋‚œ๋‹ค. 

2. TOP๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ์Šคํฌ๋กค๋ฆฌ ์ตœ์ƒ๋‹จ์œผ๋กœ ์˜ฌ๋ผ๊ฐ„๋‹ค. 

 

์šฐ์„  ์Šคํฌ๋กค์˜ ๊ฐ’์„ ์•Œ๊ธฐ ์œ„ํ•ด ScrollY ๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ state๋ฅผ ๋งŒ๋“ฆ๋‹ˆ๋‹ค. 

๊ทธ๋ฆฌ๊ณ  ํ•จ์ˆ˜๋ฅผ ํ•˜๋‚˜ ์ •์˜ํ•˜์—ฌ window.pageYoffset ๊ฐ’์„ ScrollY ์— ์ €์žฅ ํ•ฉ๋‹ˆ๋‹ค. 

 

์ด ๋ถ€๋ถ„์„ ์Šคํฌ๋กค ํ• ๋•Œ๋งˆ๋‹ค ๋ฐ˜๋ณตํ•˜๋ฉด window์˜ ์Šคํฌ๋กค ๊ฐ’์ด ScrollY์— ์ €์žฅ๋˜๊ณ , ScrollY์˜ ๊ฐ’์ด ์ €์žฅ๋˜๋ฉด์„œ useEffect๋ฅผ ํ†ตํ•ด ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ฐ”๋€Œ๋Š” ScrollY์˜ ๊ฐ’์„ ์•Œ ์ˆ˜ ์žˆ๊ฒ ์ง€์š”? 

 

 

  const [ScrollY, setScrollY] = useState(0);  // ์Šคํฌ๋กค๊ฐ’์„ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•œ ์ƒํƒœ
  const handleFollow = () => {
    setScrollY(window.pageYOffset); // window ์Šคํฌ๋กค ๊ฐ’์„ ScrollY์— ์ €์žฅ
  }

  useEffect(() => {
    console.log("ScrollY is ", ScrollY); // ScrollY๊ฐ€ ๋ณ€ํ™”ํ• ๋•Œ๋งˆ๋‹ค ๊ฐ’์„ ์ฝ˜์†”์— ์ถœ๋ ฅ
  }, [ScrollY])

  useEffect(() => {
    const watch = () => {
      window.addEventListener('scroll', handleFollow);
    }
    watch(); // addEventListener ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰
    return () => {
      window.removeEventListener('scroll', handleFollow); // addEventListener ํ•จ์ˆ˜๋ฅผ ์‚ญ์ œ
    }
  })

์ง€๊ธˆ ๊ฐ™์ด ์Šคํฌ๋กค์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ์ฒ˜๋Ÿผ ํ•œ ๋ฒˆ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ ์—ฌ๋Ÿฌ๋ฒˆ addEventListener๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ์—๋Š” removeEventListener๋ฅผ ๊ผญ ํ•ด์ฃผ์„ธ์š”! 

๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์—ฌ๋Ÿฌ๋ฒˆ ํ˜ธ์ถœ ๋˜๊ณ , ๋ฉ”๋ชจ๋ฆฌ์— gabarge collect

 

๊ฒฐ๊ณผ ์ด๋ฏธ์ง€

 

์ด๋ ‡๊ฒŒ ์Šคํฌ๋กค์˜ ์œ„์น˜๊ฐ’์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 

๊ทธ๋Ÿฌ๋ฉด ๋‚˜๋จธ์ง€๋Š” ๋” ์‰ฝ์ง€์š”. 

์ €๋Š” ์ž„์˜๋กœ ์Šคํฌ๋กค์ด 100์ •๋„ ๋‚ด๋ ค๊ฐ€๋ฉด ๋ฒ„ํŠผ์ด ๋‚˜ํƒ€๋‚˜๊ฒŒ ํ•˜์˜€์Šต๋‹ˆ๋‹ค. 

 

์ƒํƒœ์— ๋”ฐ๋ผ ๋ฒ„ํŠผ์„ ๋ณด์ด๊ณ  ์•ˆ๋ณด์ด๊ณ ๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” BtnStatus ๊ฐ’์„ ํ•˜๋‚˜ ๋‘์—ˆ์Šต๋‹ˆ๋‹ค. 

๋ฌผ๋ก  ์•„๋ž˜์˜ ์˜ˆ์‹œ์ฒ˜๋Ÿผ ๋ฒ„ํŠผ์„ ์•„์˜ˆ ๋ Œ๋”๋ฅผ ํ•˜๋Š๋ƒ ์•ˆํ•˜๋Š๋ƒ๋กœ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

{ BtnStatus &&<button className="topBtn">TOP</button> }

ํ•˜์ง€๋งŒ ์ €๋Š” opcity์™€ transition์„ ํ†ตํ•ด ์ข€ ๋” ๋ถ€๋“œ๋Ÿฌ์šด ํšจ๊ณผ๋ฅผ ์ฃผ๊ณ  ์‹ถ์–ด์„œ ๊ทธ๋ƒฅ ํด๋ž˜์Šค๋กœ ์ œ์–ดํ•˜์˜€์Šต๋‹ˆ๋‹ค. 

import { useState, useEffect } from 'react'
import './App.css';

function App() {

  const [ScrollY, setScrollY] = useState(0);
  const [BtnStatus, setBtnStatus] = useState(false); // ๋ฒ„ํŠผ ์ƒํƒœ
  
  const handleFollow = () => {
    setScrollY(window.pageYOffset);
    if(ScrollY > 100) {
      // 100 ์ด์ƒ์ด๋ฉด ๋ฒ„ํŠผ์ด ๋ณด์ด๊ฒŒ
      setBtnStatus(true);
    } else {
      // 100 ์ดํ•˜๋ฉด ๋ฒ„ํŠผ์ด ์‚ฌ๋ผ์ง€๊ฒŒ
      setBtnStatus(false);
    }
  }

  const handleTop = () => {  // ํด๋ฆญํ•˜๋ฉด ์Šคํฌ๋กค์ด ์œ„๋กœ ์˜ฌ๋ผ๊ฐ€๋Š” ํ•จ์ˆ˜
    window.scrollTo({
      top: 0,
      behavior: "smooth"
    });
    setScrollY(0);  // ScrollY ์˜ ๊ฐ’์„ ์ดˆ๊ธฐํ™”
    setBtnStatus(false); // BtnStatus์˜ ๊ฐ’์„ false๋กœ ๋ฐ”๊ฟˆ => ๋ฒ„ํŠผ ์ˆจ๊น€
  }

  useEffect(() => {
    const watch = () => {
      window.addEventListener('scroll', handleFollow)
    }
    watch();
    return () => {
      window.removeEventListener('scroll', handleFollow)
    }
  })

  return (
    <div className="wrap">
      <button 
        className={BtnStatus ? "topBtn active" : "topBtn"} // ๋ฒ„ํŠผ ๋…ธ์ถœ ์—ฌ๋ถ€
        onClick={handleTop}  // ๋ฒ„ํŠผ ํด๋ฆญ์‹œ ํ•จ์ˆ˜ ํ˜ธ์ถœ
      >TOP</button>
      <div class="inner">
        <h1>Fixed Scroll Top</h1>
          Lorem ipsum dolor sit amet consectetur adipisicing elit. Mollitia, voluptas ratione libero earum alias, officia temporibus magni possimus atque rem distinctio autem unde eaque. Minus quaerat odio mollitia asperiores neque?
          ...
      </div>
    </div>
  );
}

export default App;

 

์ด๋ ‡๊ฒŒ ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์ƒ๊ฐ ๋ณด๋‹ค ์†Œ์Šค๋„ ์งง๊ณ  ๊ฐ„ํŽธํ•ด์„œ ๋งˆ์Œ์— ๋“ญ๋‹ˆ๋‹ค. 

๋ฌผ๋ก  Scroll ๋ถ€๋ถ„๋งŒ ๋”ฐ๋กœ ์ปดํฌ๋„ŒํŠธ๋กœ ๊ด€๋ฆฌ๋ฅผ ํ•˜๋ฉด ๋”์šฑ ์ข‹๊ฒ ๋„ค์š”! 

์ €๋Š” ์ƒ˜ํ”Œ ์†Œ์Šค๋ผ ๊ทธ๋ƒฅ ํ•œ ๊ณณ์— ์ž‘์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค. 

 

scroll ๊ด€๋ จํ•˜์—ฌ ๋งŽ์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋„ ์žˆ๊ณ  ref๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฐ ํ”„๋กœ์ ํŠธ ์ƒํ™ฉ์— ๋งž๊ฒŒ ์ฐพ์•„์„œ ์ ์šฉํ•˜์‹œ๋ฉด ๋˜๊ฒ ์Šต๋‹ˆ๋‹ค. 

์ €๋Š” ์ค‘์š”ํ•˜๊ฒŒ ์ปจํŠธ๋กค ํ•˜๋Š” ๋ถ€๋ถ„์ด ์•„๋‹ˆ๋ผ์„œ ์ตœ๋Œ€ํ•œ ์†์ด ๋œ ๊ฐ€๊ณ  ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์„ ์ฐพ๋‹ค๋ณด๋‹ˆ ์ด๋ ‡๊ฒŒ ๊น”๋”ํ•˜๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ์†Œ์Šค๋ฅผ ์ฐพ์•„์„œ ๊ณต์œ  ๋“œ๋ฆฝ๋‹ˆ๋‹ค. 

์˜ค๋Š˜๋„ ์ด๋ ‡๊ฒŒ ํ•œ ํ† ๋ง‰ ๋ฐฐ์šฐ๋„ค์š”! 

ํ•ด๋‹น ์†Œ์Šค๊ฐ€ ๋„์›€์ด ๋˜์…จ๋‹ค๋ฉด, ์ €๋„ ๊ธฐ์ฉ๋‹ˆ๋‹ค! ๐Ÿฅฐ

 

๊ทธ๋Ÿผ ๋‹ค๋“ค ์ฆ๊ฑฐ์šด ์ฝ”๋”ฉ ํ•˜์„ธ์š”! 

๋ฐ˜์‘ํ˜•