HooksuseIsMounted

useIsMounted

์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งˆ์šดํŠธ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” Hook์ž…๋‹ˆ๋‹ค. SSR ํ™˜๊ฒฝ์—์„œ hydration ์—๋Ÿฌ๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์–ธ์ œ ์‚ฌ์šฉํ•˜๋‚˜์š”?

  • ๐Ÿ–ฅ๏ธ SSR ํ™˜๊ฒฝ์—์„œ ํด๋ผ์ด์–ธํŠธ ์ „์šฉ ์ฝ”๋“œ ์‹คํ–‰ ์‹œ
  • ๐Ÿ“ฆ localStorage, sessionStorage ์ ‘๊ทผ ์‹œ
  • ๐ŸŒ window, navigator ๋“ฑ ๋ธŒ๋ผ์šฐ์ € API ์‚ฌ์šฉ ์‹œ
  • ๐Ÿ• ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„์˜ ์‹œ๊ฐ„์ด ๋‹ค๋ฅผ ๋•Œ

๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

import { useIsMounted } from '@frontend-toolkit-js/hooks';
 
function Clock() {
  const isMounted = useIsMounted();
 
  // ์„œ๋ฒ„์—์„œ๋Š” false, ํด๋ผ์ด์–ธํŠธ์—์„œ๋Š” true
  if (!isMounted) {
    return <div>Loading...</div>;
  }
 
  // ํด๋ผ์ด์–ธํŠธ์—์„œ๋งŒ ์‹คํ–‰
  return <div>{new Date().toLocaleString()}</div>;
}

์ฃผ์š” ์‚ฌ์šฉ ์‚ฌ๋ก€

1. localStorage ์ ‘๊ทผ

function UserPreferences() {
  const isMounted = useIsMounted();
  const [theme, setTheme] = useState('light');
 
  useEffect(() => {
    if (isMounted) {
      const saved = localStorage.getItem('theme');
      if (saved) setTheme(saved);
    }
  }, [isMounted]);
 
  return <div className={theme}>...</div>;
}

2. ๋ธŒ๋ผ์šฐ์ € API ์‚ฌ์šฉ

function GeolocationComponent() {
  const isMounted = useIsMounted();
  const [location, setLocation] = useState(null);
 
  useEffect(() => {
    if (isMounted && 'geolocation' in navigator) {
      navigator.geolocation.getCurrentPosition(pos => {
        setLocation(pos);
      });
    }
  }, [isMounted]);
 
  return <div>...</div>;
}

3. ์„œ๋ฒ„/ํด๋ผ์ด์–ธํŠธ ์‹œ๊ฐ„ ์ฐจ์ด ํ•ด๊ฒฐ

function ServerSafeTime() {
  const isMounted = useIsMounted();
 
  // ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ์˜ ์‹œ๊ฐ„์ด ๋‹ค๋ฅด๋ฉด hydration ์—๋Ÿฌ ๋ฐœ์ƒ
  if (!isMounted) {
    return <div>--:--:--</div>;
  }
 
  return <div>{new Date().toLocaleTimeString()}</div>;
}

API Reference

function useIsMounted(): boolean;

๋ฐ˜ํ™˜๊ฐ’

  • ํƒ€์ž…: boolean
  • ์„œ๋ฒ„: false
  • ํด๋ผ์ด์–ธํŠธ (๋งˆ์šดํŠธ ํ›„): true

๋‚ด๋ถ€ ๋™์ž‘

export function useIsMounted(): boolean {
  const [mounted, setMounted] = useState(false);
 
  useEffect(() => {
    setMounted(true);
  }, []);
 
  return mounted;
}
  1. ์ดˆ๊ธฐ ๋ Œ๋”๋ง: false ๋ฐ˜ํ™˜
  2. ๋งˆ์šดํŠธ ํ›„ useEffect ์‹คํ–‰: true๋กœ ๋ณ€๊ฒฝ
  3. ๋ฆฌ๋ Œ๋”๋ง: true ๋ฐ˜ํ™˜

์ฃผ์˜์‚ฌํ•ญ

โŒ ๋‚จ์šฉํ•˜์ง€ ๋งˆ์„ธ์š”

๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ์— ์ถ”๊ฐ€ํ•˜๋ฉด ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

// โŒ Bad: ๋‹จ์ˆœ ๋ Œ๋”๋ง์—๋Š” ๋ถˆํ•„์š”
function SimpleComponent() {
  const isMounted = useIsMounted();
  return <div>Hello</div>;
}
 
// โœ… Good: ๋ธŒ๋ผ์šฐ์ € API ์‚ฌ์šฉ ์‹œ์—๋งŒ
function BrowserAPIComponent() {
  const isMounted = useIsMounted();
 
  useEffect(() => {
    if (isMounted) {
      // ๋ธŒ๋ผ์šฐ์ € API ์‚ฌ์šฉ
    }
  }, [isMounted]);
}

๐Ÿ”„ ๋ฆฌ๋ Œ๋”๋ง ๋ฐœ์ƒ

useIsMounted๋Š” ์ดˆ๊ธฐ ๋งˆ์šดํŠธ ์‹œ 1ํšŒ ๋ฆฌ๋ Œ๋”๋ง์„ ์œ ๋ฐœํ•ฉ๋‹ˆ๋‹ค.

1. ์„œ๋ฒ„/ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐ ๋ Œ๋”: isMounted = false
2. useEffect ์‹คํ–‰: setState(true)
3. ๋ฆฌ๋ Œ๋”๋ง: isMounted = true

๋Œ€์•ˆ: suppressHydrationWarning

๊ฐ„๋‹จํ•œ ๊ฒฝ์šฐ React์˜ suppressHydrationWarning์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// useIsMounted ๋Œ€์‹ 
<div suppressHydrationWarning>{new Date().toLocaleString()}</div>

๋‹จ, ์ด ๋ฐฉ๋ฒ•์€:

  • ๊ฒฝ๊ณ ๋งŒ ์ˆจ๊น€ (์‹ค์ œ hydration ๋ถˆ์ผ์น˜๋Š” ๋ฐœ์ƒ)
  • ๊ฐ„๋‹จํ•œ ํ…์ŠคํŠธ ์ฐจ์ด์—๋งŒ ์‚ฌ์šฉ ๊ถŒ์žฅ

์„ฑ๋Šฅ ํŠน์„ฑ

๋ฒˆ๋“ค ํฌ๊ธฐ

  • ~0.2 KB (minified)
  • ~0.1 KB (gzipped)

๋ฉ”๋ชจ๋ฆฌ

  • useState 1๊ฐœ๋งŒ ์‚ฌ์šฉ
  • ํด๋ฆฐ์—… ๋ถˆํ•„์š”

๋ Œ๋”๋ง

  • ์ดˆ๊ธฐ ๋งˆ์šดํŠธ ์‹œ 1ํšŒ๋งŒ ๋ฆฌ๋ Œ๋”๋ง
  • ์ดํ›„ ๋ณ€๊ฒฝ ์—†์Œ

TypeScript

const isMounted = useIsMounted();
//    ^? boolean (์ž๋™ ์ถ”๋ก )
 
if (isMounted) {
  // ํƒ€์ž… ๊ฐ€๋“œ ์—†์ด ๋ฐ”๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  localStorage.getItem('key');
}

๊ด€๋ จ ๋ฌธ์„œ


์ฐธ๊ณ  ์ž๋ฃŒ