Frontend 📚/React

[React]GitHub Pages + React로 나만의 포트폴리오 만들기 - 3. Header 구현 (useref 를 이용한 컴포넌트 간 스크롤 이동) + 최상단 이동버튼

leejaejae 2024. 9. 18. 14:10

1. Header 구현

Header.js

import React from "react";
import "../Layout_css/Header.css";

function Header() {
  return (
    <header className="menu-bar">
      <div className="menu-contents">
        <div className="menu-title">
          <a href="#Home" style={{ fontWeight: "bold" }}>
            About JAEJAE
          </a>
        </div>
        <div className="menu-list">
          <div className="menu-item">
            <a href="#Archiving">Archiving</a>
          </div>
          <div className="menu-item">
            <a href="#Skills">Tech Skills</a>
          </div>
          <div className="menu-item">
            <a href="#Project">Project</a>
          </div>
          <div className="menu-item">
            <a href="#Contact">Contact</a>
          </div>
        </div>
      </div>
    </header>
  );
}

export default Header;

- <a href="#Home"></a> 을 이용해 id="Home"인 div name="Home"인 a 태그로 이동이 가능하도록 함

Header.css

.menu-bar {
  position: fixed;
  top: 0;
  right: 0;
  left: 0;
  background-color: beige;
  z-index: 1000;
  padding: 0.5rem 1rem;
  box-sizing: border-box;
}

.menu-contents {
  width: 100%;
  display: flex;
  padding: 0.6rem 1rem;
  box-sizing: border-box;
  justify-content: space-around;
}

.menu-list {
  display: flex;
  flex-direction: row;
  justify-content: space-around;
}

.menu-title > a {
  color: black;
  font-size: clamp(1rem, 2.5vw, 1.75rem);
  cursor: pointer;
  text-decoration: none;
}

.menu-item {
  margin: 0 1rem; /* 반응형 마진 */
}

.menu-item > a {
  color: black;
  font-size: clamp(0.75rem, 2vw, 1.25rem); /* 최소 1rem, 최대 1.5rem */
  cursor: pointer;
  text-decoration: none;
}

 

2. useref를 이용한 컴포넌트 간 스크롤 이동

- useRef 훅을 이용한 내부 링크 이동 구현
- 스크롤 애니메이션을 주면서 간단하게 함수로 내부링크 이동을 구현 가능

- 근데 난 App.js에 ref 객체와 스크롤 이동 함수를 구현해 놓고각 컴포넌트에 props로 넘겨주고, 각 컴포넌트에서 forwardRef를 이용해 ref를 전달받는 식으로 구현함

App.js

import React, { useRef, useState, useEffect } from "react";

import "./App.css";
import Header from "./components/Layout/Header";

import AboutDetail from "./Details/AboutDetail";
...

function App() {
  const HomeRef = useRef(null);
  ...
  const moveToHomeHandler = () => {
    HomeRef.current.scrollIntoView({ behavior: "smooth" });
  };
  ...
  return (
    <div className="App">
      <Header
        moveToHome={moveToHomeHandler}
        ...
      />
      <AboutDetail ref={HomeRef} moveToArc={moveToHomeHandler} />
      <div
        ref={ArchivingRef}
        ...
      >
        <ArchivingDetail />
      </div>
      ...
    </div>
  );
}

export default App;

AboutDetail.js

import React, { forwardRef } from "react";
import "../Details_css/AboutDetail.css";

const aboutDetail = forwardRef((props, ref) => {
  return (
    <div className="home" ref={ref}>
      <div className="home-myPhoto">
        ...
      </div>
      <div className="home-about">
        ...
      </div>
    </div>
  );
});

export default aboutDetail;

- 컴포넌트 전체를 forwardRef();로 감싸주면 됨
- 나머지 컴포넌트도 동일!

 

3. 최상단 이동버튼

1) react-icons 적용

https://react-icons.github.io/react-icons/

 

React Icons

 

react-icons.github.io


2) 개별 컴포넌트 구현

TopButton.js

import { useEffect, useState } from "react";
import { SlArrowUp } from "react-icons/sl";

import "./TopButton.css";

const TopButton = () => {
  const [isVisible, setIsVisible] = useState(false);

  const moveToTop = () => {
    window.scrollTo({ top: 0, behavior: "smooth" });
  };

  const handleScroll = () => {
    const scrollTop = window.pageYOffset;
    if (scrollTop >= 40) {
      setIsVisible(true);
    } else {
      setIsVisible(false);
    }
  };

  useEffect(() => {
    // 스크롤 이벤트 리스너를 추가
    window.addEventListener("scroll", handleScroll);

    // 컴포넌트가 언마운트되거나 업데이트되기 직전에 호출되는 함수를 반환
    // 이 함수에서는 스크롤 이벤트 리스너를 제거
    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, []);

  return (
    <div className="top-btn" onClick={moveToTop} isVisible={isVisible}>
      <SlArrowUp />
    </div>
  );
};

export default TopButton;

 

4. 결과 🎉