⏩ 포트폴리오를 제작하면서 예전부터 구현해보고 싶었던 다크 모드/라이트 모드를 구현해보려 한다.
찾다보니 이미 npm dark mode 라이브러리가 있었지만, 직접 구현해보고 싶어서 해당 라이브러리는 사용하지 않았다.
가장 먼저 toggle button이 필요해서 material-ui 에서 이미 제작된 toggle 액션이 가능한 버튼을 가져왔고 색상이나 크기는 커스텀하여 작성해뒀다.
//기능 구현 전 / Toggle component
import React, { useState } from "react";
import { withStyles } from "@material-ui/core/styles";
import FormControlLabel from "@material-ui/core/FormControlLabel";
import Switch from "@material-ui/core/Switch";
const OrangeSwitch = withStyles({
root: {
width: "60px",
height: "40px",
},
switchBase: {
color: "#F54404",
"&$checked": {
color: "#F54404",
},
"&$checked + $track": {
backgroundColor: "#F54404",
},
"&$focusVisible $thumb": {
border: "6px solid #F54404",
},
},
track: {
border: `1px solid black`,
backgroundColor: "white",
},
checked: {},
})(Switch);
export default function CustomizedSwitches() {
const [isChecked, setIsChecked] = useState(false);
const handleChange = () => {
setIsChecked(!isChecked);
};
return (
<div>
<FormControlLabel
control={
<OrangeSwitch
checked={isChecked}
onChange={handleChange}
name="checkedA"
/>
}
label="Dark Mode !"
/>
</div>
);
}
⏩ 버튼은 준비되었고 이제 기능을 구현하면 된다. 다크모드와 라이트모드를 위해서 우선 theme.js 파일에 다크모드와 라이트모드에서 사용될 컬러를 지정해주어야 한다.
// theme.js 일부
const lightTheme = {
bgColor: "#F1EFED",
textColor: "black",
};
const darkTheme = {
bgColor: "#181818",
textColor: "white",
};
export const theme = {
...lightTheme,
...darkTheme,
};
export default theme;
⏩ 만들어둔 theme을 global.style에 적용해준다.
//global.style
import { createGlobalStyle } from "styled-components";
const GlobalStyle = createGlobalStyle`
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
html,body {
height:100%;
}
body {
box-sizing: border-box;
font-family: 'Syncopate','Noto Sans KR', sans-serif;
background-color: #F1EFED;
background: ${({ theme }) => theme.bgColor};
color: ${({ theme }) => theme.textColor};
transition: all 0.25s linear;
}
a {
text-decoration:none;
color: ${({ theme }) => theme.textColor};
}
li {
list-style:none;
}
button {
outline:none;
border:none;
color: ${({ theme }) => theme.textColor};
background-color:transparent;
}
`;
export default GlobalStyle;
- 관련 스타일은 모두 작성이 되었고 이제 상태를 확인하여 유저가 button을 클릭할 때 다크모드, 라이트모드로 변경하면 됐다. 이 과정을 고민하던 중 theme과 toggle 컴포넌트가 계속 props로 전달하여 상태를 주고 받는것이 비효율적이라고 생각해서 useContext hook을 사용하여 바로 상태를 주고받을 수 있도록 하는 방법을 생각했다.
(😱 ⏩ 현재 styled-components를 theme을 만들어서 아래와 같이 사용하고 있었다. 여기서 ThemeProvider은 그대로 두고 외부에 다시 ThemeContext를 감싸면 되는데, 이 두가지를 합쳐서 적용해야 된다는 생각 때문에 삽질을 오래 했다..)
//App.js 일부
<ThemeProvider theme={theme}>
<GlobalStyle theme={mode === darkTheme ? darkTheme : lightTheme} />
<Layout />
</ThemeProvider>
⏩ 결국 따로 ThemeContext을만들어서 외부에서 감싸는 방식을 선택했는데, 두가지 같이 사용할 수 있는 방법이 있는지 아직도 궁금하다..
import React from "react";
import { ThemeProvider } from "styled-components";
import { useLightMode } from "./hooks/useLightMode";
import Layout from "./components/Layout";
import GlobalStyle from "./Styles/Global.style";
import { theme } from "./Styles/theme";
import { ThemeContext } from "./context/ThemeContext";
const { darkTheme, lightTheme } = theme;
function App() {
const { mode, toggleTheme } = useLightMode(); //useLightMode hook
return (
<>
<ThemeContext.Provider value={{ mode, toggleTheme }}>
<ThemeProvider theme={theme}>
<GlobalStyle theme={mode === darkTheme ? darkTheme : lightTheme} />
<Layout />
</ThemeProvider>
</ThemeContext.Provider>
</>
);
}
export default App;
//themeContext.js
import { createContext } from "react";
import { theme } from "../Styles/theme";
const { darkTheme } = theme;
export const ThemeContext = createContext({
mode: darkTheme,
toggleTheme: () => {
return null;
},
});
⏩ 또한, ToggleButton 컴포넌트 내부에서 사용하던 로직을 useLightMode hook을 만들어서 사용했다.
//useLigthMode.js
import { useState } from "react";
import { theme } from "../Styles/theme";
export const useLightMode = () => {
const { lightTheme, darkTheme } = theme;
const [mode, setMode] = useState(darkTheme);
const toggleTheme = () => {
mode === darkTheme ? setMode(lightTheme) : setMode(darkTheme);
};
return { mode, toggleTheme };
};
✅ 기능이 구현된 ToggleButton 컴포넌트
//ToggleSwitch 컴포넌트
...
function ToggleSwitch() {
const { mode, toggleTheme } = useContext(ThemeContext);
return (
<div>
<FormControlLabel
control={
<OrangeSwitch onChange={toggleTheme} name="checkedA" mode={mode} />
}
label={
mode.textColor === "white"
? "Switch to Light mode"
: "Switch to Dark mode"
}
/>
</div>
);
}
export default ToggleSwitch;
'React' 카테고리의 다른 글
Netlify로 react 프로젝트 배포하기 (0) | 2021.03.03 |
---|---|
react 프로젝트, 크로스브라우징 대응하기 (0) | 2021.03.02 |
Controlled components와 Uncontrolled components (0) | 2021.02.17 |
React 프로젝트 진행시 API_key 숨기기 (0) | 2021.02.14 |
[React 강의노트] react router (0) | 2021.01.02 |