๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ“— React

[React] sticky Header CSS๊ฐ€ ๋ณ€ํ•˜๋Š” sticky Header ๋งŒ๋“ค๊ธฐ์™€ ์ตœ์ ํ™”

by Tamii 2022. 5. 17.
๋ฐ˜์‘ํ˜•

 

์—ฌ๋Ÿฌ ์›น์‚ฌ์ดํŠธ์—์„œ๋Š” scroll๊ณผ ๊ด€๊ณ„์—†์ด ์ƒ๋‹จ์— ๋ถ™์–ด์žˆ๋Š” sticky header๋ฅผ ์ž์ฃผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š”๋ฐ์š”,

์•„๋ž˜ ์˜ˆ์‹œ์™€ ๊ฐ™์ด ํŠน์ • ์˜์—ญ์—์„œ css๊ฐ€ ๋ฐ”๋€” ์ˆ˜ ์žˆ๋Š” header๋ฅผ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. 

 

โš™๏ธ ์‚ฌ์šฉ ์Šคํƒ : React, emotion/css

 

 

 

 

 

๐Ÿ“Œ ๊ธฐ๋ณธ ๊ตฌ์กฐ ์ƒ์„ฑํ•˜๊ธฐ

ํ—ค๋”๋Š” ๊ณตํ†ต์ ์œผ๋กœ ์‚ฌ์šฉํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’๊ธฐ ๋•Œ๋ฌธ์— Header๋กœ ๋ถ„๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.

 

App.js

import { css } from "@emotion/react";
import Header from "./Header.jsx";


export default function App() {

  return (
    <div className="App" css={wrapStyle}>
      <Header />
    </div>
  );
}

const wrapStyle = css`

`;

Header.jsx

import { css } from "@emotion/react";

export default function Header() {

  return <header css={headerStyle}>Header</header>;
}

const headerStyle = (isPoint) => css`
`;

 

 

๐Ÿ“Œ ์ƒ๋‹จ์— Header ๊ณ ์ •์‹œํ‚ค๊ธฐ

CSS๋ฅผ ์•„๋ž˜์™€ ๊ฐ™์ด ์„ค์ •ํ•˜๊ณ  customํ•œ ์„ค์ •๋“ค์„ ํ•˜๋ฉด Header๋Š” ๊ฐ€์žฅ ์ƒ๋‹จ์— ๋ถ™์–ด์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

position : sticky

top : 0

import { css } from "@emotion/react";

export default function Header() {

  return <header css={headerStyle}>Header</header>;
}

const headerStyle = (isPoint) => css`
  height: 44px;
  position: sticky;
  top: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: #e0e0e0;
`;

 

 

๐Ÿ“Œ Header์˜ CSS ๊ฐ€ ๋ฐ”๋€” ์กฐ๊ฑด ์„ค์ •ํ•˜๊ธฐ

 

scroll์ด ์ƒ๊ธธ๋งŒํ•œ body๋ฅผ ๋งŒ๋“ค๊ณ ์ž 30 length์˜ ๋ฐฐ์—ด์„ ๋งŒ๋“ค๊ฒ ์Šต๋‹ˆ๋‹ค.

๋ชฉํ‘œ๋Š” " 15๋ฒˆ์งธ <h1>์„ ๋งŒ๋‚˜๋ฉด Header์˜ ์ƒ‰์ƒ์ด ๋ณ€๊ฒฝ๋˜๋Š” ๊ฒƒ "

 

App.js

import { css } from "@emotion/react";
import Header from "./Header.jsx";
import { useRef } from "react";

export default function App() {
  const pointRef = useRef(null);
  return (
    <div className="App" css={wrapStyle}>
      <Header pointRef={pointRef} />
      {new Array(30).fill(0).map((ele, idx) => (
        <h1
          key={idx}
          className={idx >= 15 ? "coloredText" : "text"}
          ref={idx === 15 ? pointRef : null}
        >
          {idx}
        </h1>
      ))}
    </div>
  );
}

const wrapStyle = css`
  .text {
    background-color: #f5f5f5;
  }
  .coloredText {
    background-color: #ffb600;
  }
`;

 

Header.jsx

  • setPointHeader - point์ง€์  (ex 15๋ฒˆ์งธ h1) window scroll์ด  pointRef์— ๋„๋‹ฌํ•˜๋Š” ์ˆœ๊ฐ„ isPoint true ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. 
  • useEffect - addEventListener์—๋Š”  return removeEventListener๋„ ํ•ด์ค˜์•ผ ํ•˜๋Š”๋ฐ ๊ทธ ์ด์œ ๋Š” React๊ฐ€ ์žฌ๋ Œ๋”๋ง ๋  ๋•Œ๋งˆ๋‹ค  ์ด๋ฒคํŠธ ๋Œ€์ƒ์— ๊ณ„์† ๊ฐ™์€ ํ•จ์ˆ˜๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ๋•Œ๋ฌธ์—  react component๊ฐ€ unmounted ๋˜๋Š” ์‹œ์ ์— ์ •ํ™•ํžˆ ์ œ๊ฑฐ๋˜์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
import { useEffect, useState, useMemo } from "react";

import { css } from "@emotion/react";

export default function Header({ pointRef }) {
  const HEADER_HEIGHT = 44;
  const [isPoint, setIsPoint] = useState(false);

  const setPointHeader = useMemo(
    () =>{
        const isReachPoint =
          window.scrollY > pointRef.current.offsetTop - HEADER_HEIGHT;
        if (isReachPoint !== isPoint) setIsPoint(isReachPoint);
      }
    , [isPoint]
  );

  useEffect(() => {
    window.addEventListener("scroll", setPointHeader);

    return () => {
      window.removeEventListener("scroll", setPointHeader);
    };
  }, [isPoint, setPointHeader, pointRef]);

  return <header css={headerStyle(isPoint)}>Header</header>;
}

const headerStyle = (isPoint) => css`
  height: 44px;
  position: sticky;
  top: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: ${isPoint ? "#ffb600" : "#e0e0e0"};
`;

 

 

 

๐Ÿ“Œ throttle๋กœ ์ตœ์ ํ™”

๊ทธ๋Ÿผ์—๋„ scoll event๋Š” scroll ํ•  ๋•Œ๋งˆ๋‹ค ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ถ”๊ฐ€์ ์ธ ์ตœ์ ํ™”๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

  • throttle: ๊ฐ™์€ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐ˜๋ณต ์‹คํ–‰๋  ๋•Œ , ์ž„์˜ ์„ค์ •ํ•œ ์‹œ๊ฐ„ ๊ฐ„๊ฒฉ (ms) ๋‚ด์—์„œ ํ•œ๋ฒˆ์˜ ์ฝœ๋ฐฑํ•จ์ˆ˜๋งŒ ํ˜ธ์ถœํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

 

Header.jsx

๋”ฐ๋ผ์„œ ์•„๋ž˜์™€ ๊ฐ™์ด 0.3์ดˆ ์ •๋„๋กœ  ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

// ์ค‘๋žต

const setPointHeader = useMemo(
    () =>
      throttle(() => {
        const isReachPoint =
          window.scrollY > pointRef.current.offsetTop - HEADER_HEIGHT;
        if (isReachPoint !== isPoint) setIsPoint(isReachPoint);
      }, 300),
    [isPoint]
  );

 

 

 

 

๐Ÿฅณ ๊ทธ๋Ÿผ ์™„๋ฃŒ

code๋กœ ์ง์ ‘ ํ™•์ธํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ink_0์˜ codesandbox - [React] sticky header change CSS in point ๋กœ ํ™•์ธํ•ด์ฃผ์„ธ์š”

 

[React] sticky header change CSS in point - CodeSandbox

[React] sticky header change CSS in point by ink-0 using @emotion/core, @emotion/css, @emotion/react, emotion, lodash, react, react-dom, react-scripts

codesandbox.io

 

 

 

 

 

 

 

์ฐธ๊ณ  ๋งํฌ

๐Ÿ”— [React] ๋ฆฌ์•กํŠธ ํ—ค๋” ์Šคํฌ๋กค ์‹œ CSS ๋ณ€๊ฒฝ

 

[React] ๋ฆฌ์•กํŠธ ํ—ค๋” ์Šคํฌ๋กค ์‹œ CSS ๋ณ€๊ฒฝ

 ํ—ค๋” ์Šคํฌ๋กค ์‹œ ๋””์ž์ธ ๋ณ€๊ฒฝ Vue์—์„œ ์ œ์ž‘ํ•œ ์ฝ”๋“œ๋ฅผ React๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์ž‘์—… ์ค‘์ธ๋ฐ ์ฝ”๋“œ๊ฐ€ ๋‹ฌ๋ผ์„œ ๋ฆฌ์•กํŠธ ๋ฒ„์ ผ์œผ๋กœ๋„ ์˜ฌ๋ ค๋ณธ๋‹ค. iancoding.tistory.com/216 [Vue] ์Šคํฌ๋กค ์‹œ ํ—ค๋” ์ƒ‰ ๋ณ€๊ฒฝ 1. ๋จผ์ € data์— scrol

iancoding.tistory.com

๐Ÿ”— [React] scroll ์ด๋ฒคํŠธ throttle๋กœ ์ตœ์ ํ™” ์‹œํ‚ค๊ธฐ

 

[React] scroll ์ด๋ฒคํŠธ throttle๋กœ ์ตœ์ ํ™” ์‹œํ‚ค๊ธฐ

Scroll ์ด๋ฒคํŠธ ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ ์ง„ํ–‰ ์ค‘ ์Šคํฌ๋กค์„ ์–ด๋Š์ •๋„ ํ–ˆ๋Š๋ƒ์— ๋”ฐ๋ผ ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฐ”๊ฐ€ ๋‚˜ํƒ€๋‚˜๊ณ , ์‚ฌ๋ผ์ง€๊ฒŒ ํ•˜๋Š” ๊ธฐ๋Šฅ์ด ํ•„์š”ํ–ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด์„œ ์ผ๋‹จ scroll ์ด๋ฒคํŠธ๋ฅผ ๊ฑธ์–ด์ฃผ์–ด์•ผ ํ–ˆ๋Š”๋ฐ, scroll ์ด

mengkki.netlify.app

๐Ÿ”— How to Cleanup Event Listeners in React

 

How to Cleanup React Event Listeners | Pluralsight

When building a UI in React, you'll likely have many events that you want to listen to. That means you'll want to clean them up too. So, what are events, listeners, and why do we want to clean them up?

www.pluralsight.com

 

๋Œ“๊ธ€