[STYLED COMPONENTS] Recoil을 사용하지 않고 darkTheme, lightTheme 만들기
학습일 : 2023. 04. 14
Recoil을 사용하지 않고 만드는 버전을 알아보자
Recoil 없이 다크모드/라이트모드 스위치를 구현하기 위해서는
<index.tsx>
기존에 index.tsx에 있는 ThemeProvider를 App.tsx로 옮긴다.
(옮기는 이유는 어플리케이션의 state에 기반하여 바꾸기 위해서임)
이동 전
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
root.render(
<QueryClientProvider client={queryClient}>
<ThemeProvider theme={darkTheme}>
<App />
</ThemeProvider>
</QueryClientProvider>
);
이동 후
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
root.render(
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
);
<App.tsx>
이동 전
function App() {
return (
<>
<GlobalStyle />
<Router />
<ReactQueryDevtools initialIsOpen={true} />
</>
);
}
export default App;
이동 후
import { ThemeProvider, createGlobalStyle } from "styled-components";
import { darkTheme, lightTheme } from "./theme"; // import
function App() {
return (
<>
<ThemeProvider theme={darkTheme}>
<GlobalStyle />
<Router />
<ReactQueryDevtools initialIsOpen={true} />
</ThemeProvider>
</>
);
}
export default App;
이로써 theme을 render 하는 ThemeProvider를 index.tsx에서 App component로 가져오게 되었다.
<theme.ts>
darkTheme과 lightTheme을 작성해준다.
import { DefaultTheme } from "styled-components";
export const darkTheme: DefaultTheme = {
bgColor: "rgb(62 62 45)",
textColor: "#f5f6fa",
accentColor: "#e1b12c",
boxColor: "#2b281b"
}
export const lightTheme: DefaultTheme = {
bgColor: "#c5c4b8",
textColor: "black",
accentColor: "#e1b12c",
boxColor: "rgb(173 167 141)"
};
<App.tsx>
darkTheme, lightTheme을 import 해준다. (난 아까 이미 해줬음)
그리고 ThemeProvider에 theme prop으로 삼항연산자를 써줌
import { darkTheme, lightTheme } from "./theme";
function App() {
const [isDark, setIsDark] = useState(false);
return (
<>
<ThemeProvider theme={isDark ? darkTheme : lightTheme}>
<GlobalStyle />
<Router />
<ReactQueryDevtools initialIsOpen={true} />
</ThemeProvider>
</>
);
}
export default App;
이제 isDark를 true나 false로 toggle하는 function을 만들도록 한다.
<App.tsx>
setState function을 사용할 때 두가지 옵션이 있다. (참고)
1. const toggleDark = () => setIsDark(true); 그냥 이렇게 value를 보내거나
2. const toggleDark = () => setIsDark(); value 대신 function을 보내거나.
이 function은 첫번째 argument로 현재의 state를 가짐.
2. 를 사용 - 현재를 current로 두고 그 반대를 return 하도록 만든다.
isDark가 true이면 false를 return 하고, false이면 true를 return하게 된다.
const toggleDark = () => setIsDark((current) => !current);
button 을 만들어 주고 onClick으로 toggleDark를 준다.
그럼 클릭시 state가 바뀌고 theme이 바뀌게 되어서 다크모드로 전환된다.
<ThemeProvider theme={isDark ? darkTheme : lightTheme}>
<GlobalStyle />
<Router />
<ReactQueryDevtools initialIsOpen={true} />
<button onClick={toggleDark}>테마 변경</button>
</ThemeProvider>
<App.tsx>
전체 코드
function App() {
const [isDark, setIsDark] = useState(false);
const toggleDark = () => setIsDark((current) => !current);
return (
<>
<ThemeProvider theme={isDark ? darkTheme : lightTheme}>
<GlobalStyle />
<Router />
<ReactQueryDevtools initialIsOpen={true} />
<button onClick={toggleDark}>테마 변경</button>
</ThemeProvider>
</>
);
}
export default App;
실행결과
클릭시 다크모드, 라이트모드로 바뀌는 것을 확인할 수 있다.
총 정리
1. App.tsx
function 을 Router 로 보냄
import { ThemeProvider, createGlobalStyle } from "styled-components";
import Router from "./Router";
import { ReactQueryDevtools } from "react-query/devtools";
import { darkTheme, lightTheme } from "./theme";
import { useState } from "react";
function App() {
const [isDark, setIsDark] = useState(false);
const toggleDark = () => setIsDark((current) => !current);
return (
<>
<ThemeProvider theme={isDark ? darkTheme : lightTheme}>
<GlobalStyle />
<Router toggleDark={toggleDark} />
<ReactQueryDevtools initialIsOpen={true} />
</ThemeProvider>
</>
);
}
export default App;
2. Router.tsx
그다음 Router에서 Coins로 보냄
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Coins from "./routes/Coins";
import Coin from "./routes/Coin";
interface IRouterProps {
toggleDark: () => void;
}
function Router({ toggleDark }: IRouterProps) {
return (
<BrowserRouter basename={process.env.PUBLIC_URL}>
<Routes>
<Route path={`/`} element={<Coins toggleDark={toggleDark} />} />
<Route path="/:coinId/*" element={<Coin />} />
</Routes>
</BrowserRouter>
);
}
export default Router;
3. Coins.tsx
그러고 나서야 Coins에서 이 function을 가지게 된다.
import { Link } from "react-router-dom";
import styled from "styled-components";
import { useQuery } from "react-query";
import { fetchCoins } from "../api";
import { Helmet } from "react-helmet";
interface ICoinsProps {
toggleDark: () => void;
}
function Coins({toggleDark}:ICoinsProps) {
// useQuery를 통해 Coins 를 fetch
const { isLoading, data } = useQuery<ICoin[]>("allCoins", fetchCoins);
return (
<Container>
<Helmet>
<title>Thorn Coin</title>
</Helmet>
<Header>
<Link to={"/"}>
<Title>Thorn Coin</Title>
</Link>
<SubTitle>Grab Your Own Coin!</SubTitle>
<button onClick={toggleDark}>테마 변경</button>
</Header>
{isLoading ? (
<Loader>로딩중입니다...</Loader>
) : (
<CoinsList>
{data?.slice(0, 100).map((coin) => (
<Coin key={coin.id}>
<Link to={`/${coin.id}`} state={coin}>
<Img
src={` https://cryptocurrencyliveprices.com/img/$ {coin.id}.png`}
alt=""
/>
{coin.name}
</Link>
</Coin>
))}
</CoinsList>
)}
</Container>
);
}
export default Coins;