// useDarkMode.ts
import { Dispatch, SetStateAction, useEffect, useState } from "react";
function useDarkMode(): [string, Dispatch<SetStateAction<string>>] {
const [theme, setTheme] = useState(
typeof window !== "undefined" ? localStorage.theme : "dark"
);
const colorTheme = theme === "dark" ? "light" : "dark";
useEffect(() => {
const root = window.document.documentElement;
root.classList.remove(colorTheme);
root.classList.add(theme);
console.log(root);
if (typeof window !== "undefined") {
localStorage.setItem("theme", theme);
}
}, [theme]);
return [colorTheme, setTheme];
}
export default useDarkMode;
TypeScript
복사
svg로 구현하면 아래와 같이 지저분하다.
import useDarkMode from "../useDarkMode";
import Head from "next/head";
export default function Home() {
const [colorTheme, setTheme] = useDarkMode();
return (
<div>
<Head>
<title>Dark mode demo</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
{colorTheme === "light" ? (
<svg
onClick={() => setTheme("light")}
...
>
<path
...
/>
</svg>
) : (
<svg
onClick={() => setTheme("dark")}
...
>
<path
...
/>
</svg>
)}
</div>
);
}
TypeScript
복사
이 경우는 svg에 바로 onclick 이벤트를 받는다. 나는 FontAwesomeIcon 컴포넌트 가져와서 사용하고 여기에 바로 onclick 이벤트 핸들러를 줄 수 없어서 button으로 감싸서 button에 줬다.
button으로 감싸면 중복된 코드가 늘어났다.
...
return(
<>
{colorTheme === "light" ? (
<button
onClick={() => setTheme("light")}
>
<FontAwesomeIcon icon={faSun} />
</button>
) : (
<button
onClick={() => setTheme("dark")}
>
<FontAwesomeIcon icon={faMoon} />
</button>
)}
</>
);
TypeScript
복사
삼항 연산자를 이용해서 dry하게 만드려면 바로 이전 상태를 가져와 사용해야 한다.(이렇게 하지 않으면 이미 변경된 state를 사용하게 되어 정상적으로 작동되지 않는다.)
setTheme(prev ⇒ prev === “dark” ? “light” : “dark”);
TypeScript
복사
완성하면 아래와 같다.
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faSun, faMoon } from "@fortawesome/free-regular-svg-icons";
import styles from "./DarkModeButton.module.scss";
import useDarkMode from "../../hooks/useDarkMode";
const DarkModeButton = (): JSX.Element => {
const [colorTheme, setTheme] = useDarkMode();
return (
<button
className={styles.el_darkModeBtn}
onClick={() =>
setTheme((theme: string) => (theme === "dark" ? "light" : "dark"))
}
>
<FontAwesomeIcon icon={colorTheme === "light" ? faSun : faMoon} />
</button>
);
};
export default DarkModeButton;
TypeScript
복사