themeContext.tsx
import { ReactNode, createContext, useEffect, useState } from "react";
import { ThemeName, getTheme } from "../style/theme";
import { ThemeProvider } from "styled-components";
import { GlobalStyle } from "../style/global";
const DEFAULT_THEME_NAME = "light";
const THEME_LOVALSTORAGE_KEY = "book_store_theme";
// 테마상태와 토글 함수 interface 정의
interface State {
themeName: ThemeName;
toggleTheme: () => void;
}
// createContext할 때 default값이 필요해서 생성
export const state = {
themeName: "light" as ThemeName,
toggleTheme: () => {},
};
// context 만들기(테마상태/ 테마 바꾸는 함수 포함), export
// default value가 필요해서 state를 만들어서 넣은 것
// 사용할 때는 value로 값을 넣어줄 것임.
export const ThemeContext = createContext<State>(state);
// 테마 상태 총괄 provider , export
export const BookStoreProvider = ({ children }: { children: ReactNode }) => {
// 기본 값 설정, state로 테마 상태 관리
const [themeName, setThemeName] = useState<ThemeName>("light");
// reload될 때
// 로컬 스토리지에서 THEME_LOVALSTORAGE_KEY를 키로 가진 테마 상태 가져옴
// 로컬 스토리지에서 가져온 게 있다면 테마 상태 setState
useEffect(() => {
const savedThemeName = localStorage.getItem(
THEME_LOVALSTORAGE_KEY
) as ThemeName;
setThemeName(savedThemeName || DEFAULT_THEME_NAME);
}, []);
// 토글하고 setState & 로컬 스토리지에 저장
const toggleTheme = () => {
setThemeName(themeName === "light" ? "dark" : "light");
localStorage.setItem(
THEME_LOVALSTORAGE_KEY,
themeName === "light" ? "dark" : "light"
);
};
return (
<ThemeContext.Provider value={{ themeName, toggleTheme }}>
<ThemeProvider theme={getTheme(themeName)}>
<GlobalStyle themeName={themeName} />
{children}
</ThemeProvider>
</ThemeContext.Provider>
);
};
// ThemeContext.Provider
// ThemeContext는 만든 context, provider로 뿌려줌. value로 테마상태, 토글함수 넣어줌. (interface 따름)
// themeProvider
// theme 파일에서 state에 저장된 themeName으로 가져와서 themeprovider 설정
// 상태를 통해 themeName props로 넘겨주기
// styled-components 이용
// 없으면 하위 파일에서 theme 파일에서 맞는 테마 색상을 못찾음.
// 현재 테마에 따른 테마 뿌려줌? => theme 파일 알아서 찾아서?
// GlobalStyle
// global 파일에서 props에 따라 globalstyle 만들어서 export
// styled-components 에서 createGlobalStyle 이용
// ThemeSwitcher
// setThemeName 이용하여 themeName 상태 바꾸는 역할
global.ts
import "sanitize.css";
import { createGlobalStyle } from "styled-components";
import { ThemeName } from "./theme";
interface Props {
themeName: ThemeName;
}
export const GlobalStyle = createGlobalStyle<Props>`
body{
padding: 0;
margin: 0;
background-color: ${(props) =>
props.themeName === "light" ? "white" : "black"}
}
h1{
margin: 0;
}
*{
color: ${(props) => (props.themeName === "light" ? "black" : "white")}
}
`;
theme.ts
export type ThemeName = "light" | "dark";
type ColorKey = "primary" | "background" | "secondary" | "third";
interface Theme {
name: ThemeName;
color: Record<ColorKey, string>;
}
// Record<>: 매핑으로 생각하면 된다.
// 모든 타입으로 생성됨.
// 인터페이스 구현시 ColorKey의 모든 값을 키값으로 만들어야한다.
// 이 유틸리티는 타입의 프로퍼티를 다른 타입에 매핑 시키는데 사용될 수 있습니다.
export const light: Theme = {
name: "light",
color: {
primary: "brown",
background: "lightgray",
secondary: "blue",
third: "coral",
},
};
export const dark: Theme = {
name: "dark",
color: {
primary: "coral",
background: "midnightblue",
secondary: "yellow",
third: "blue",
},
};
export const getTheme = (themeName: ThemeName) => {
switch (themeName) {
case "light":
return light;
case "dark":
return dark;
}
};
themeSwitcher.tsx
import { useContext } from "react";
import { ThemeContext } from "../../context/themeContext";
const ThemeSwitcher = () => {
// context에서 가져옴
const { themeName, toggleTheme } = useContext(ThemeContext);
return (
<div>
<button onClick={toggleTheme}>{themeName}</button>
</div>
);
};
export default ThemeSwitcher;
app.tsx
import Home from "./pages/Home";
import Layout from "./components/layout/Layout";
import ThemeSwitcher from "./components/header/ThemeSwitcher";
import { BookStoreProvider } from "./context/themeContext";
function App() {
return (
<>
<BookStoreProvider>
<ThemeSwitcher />
<Layout children={<Home />} />
</BookStoreProvider>
</>
);
}
export default App;
'타입스크립트로 함께하는 웹 풀 사이클 개발(React, Node.js) > TIL' 카테고리의 다른 글
api 요청 플로우 , params 처리 (0) | 2024.03.01 |
---|---|
화면 테스트하기 (0) | 2024.03.01 |
데브코스 15주차 - [bookstore] Day3 초기설정, 폴더 구조, 레이아웃 (0) | 2024.02.25 |
데브코스 14주차 - 오픈소스 / 오픈소스 라이선스 (0) | 2024.02.21 |
데브코스 13주차 Day3, 4 - contextAPI, Redux / node.js와 연동 및 배포 (0) | 2024.02.18 |