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;
}- ์ด๊ธฐ ๋ ๋๋ง:
false๋ฐํ - ๋ง์ดํธ ํ
useEffect์คํ:true๋ก ๋ณ๊ฒฝ - ๋ฆฌ๋ ๋๋ง:
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');
}๊ด๋ จ ๋ฌธ์
- useCalendar - SSR ์์ ์ฑ ์์
- Next.js hydration ์๋ฌ