React와 TypeScript환경 프로젝트에서 다크모드와 라이트모드 상태관리 구현하는 프로젝트를 진행해 보겠습니다
1. 필요한 라이브러리들을 설치합니다.
npm install redux react-redux @types/react-redux @reduxjs/toolkit styled-components
타입스크립트를 사용하신다면 @types/react-redux를 추가로 설치해야 합니다.
2. 필요한 파일들을 폴더에 맞게 생성합니다.
저는 theme 전역변수들을 선언할 themeSlice.js파일과 각각의 slice들을 reducer에 담아둘 store.js 파일을 redux폴더에 생성했고,
전역적으로 스타일을 관리할 GlobalStyles.tsx 파일을 style폴더에 생성했습니다.
3. 리덕스로 전역변수 선언하기
// themeSlice.js
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
/* 초기 설정 */
colors: {
text: "#000000",
},
backgroundColors: {
body: "#F4F5F9",
},
darkMode: false,
};
const themeSlice = createSlice({
name: "theme",
initialState,
reducers: {
/* light mode */
setLightTheme(state) {
state.darkMode = false;
state.colors.text = "#000000";
state.backgroundColors.body = "#F4F5F9";
},
/* dark mode */
setDarkTheme(state) {
state.darkMode = true;
state.colors.text = "#ffffff";
state.backgroundColors.body = "#343638";
},
},
});
export const { setLightTheme, setDarkTheme } =
themeSlice.actions;
export default themeSlice.reducer;
초기 디폴트 변수인 initialState를 생성해 라이트모드의 스타일 값들을 주었고 createSlice를 활용하여 라이트모드와 다크모드의 스타일 값들을 주었습니다.
4. store에 저장하기
// store.js
import { configureStore } from "@reduxjs/toolkit";
import themeSlice from "./themeSlice";
const store = configureStore({
reducer: {
theme: themeSlice,
},
});
export default store;
store파일에 export 한 themeSlice를 reducer에 저장시킵니다.
5. styled-components로 전역 스타일 주기
// GlobalStyles.tsx
import { createGlobalStyle } from "styled-components";
const GlobalStyles = createGlobalStyle`
* {
color: ${({ theme }) => theme.colors.text};
background-color: ${({ theme }) => theme.backgroundColors.body};
}
`;
export default GlobalStyles;
createGlobalStyle을 활용하여 theme props를 통해 전역적으로 스타일을 관리할 수 있도록 설정합니다.
6. store에 저장한 리덕스 가져오기
// index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { Provider } from 'react-redux';
import store from './redux/store';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
src폴더에 생성되어 있는 index.tsx파일에서 redux의 Provider를 활용하여 생성한 reducer를 가져옵니다.
7. 전역 상태 변경하기
// App.tsx
import React, { useCallback } from 'react';
import { useSelector, useDispatch } from "react-redux";
import { setDarkTheme, setLightTheme } from "./redux/themeSlice";
import { ThemeProvider } from 'styled-components';
import GlobalStyles from './style/GlobalStyles';
function App() {
const theme = useSelector((state: any) => state.theme);
const dispatch = useDispatch();
// 랜더링되지 않도록 useCallback 훅을 사용하여 함수를 메모이제이션
const setDark = useCallback(() => {
dispatch(setDarkTheme());
}, [dispatch]);
const setLight = useCallback(() => {
dispatch(setLightTheme());
}, [dispatch]);
return (
<div className="App">
<ThemeProvider theme={theme}>
<GlobalStyles />
<h1>{theme.darkMode ? "다크모드 상태" : "라이트모드 상태"}</h1>
<button onClick={() => setDark()}>다크모드 켜기</button>
<button onClick={() => setLight()}>라이트모드 켜기</button>
</ThemeProvider>
</div>
);
}
export default App;
- 리덕스의 useSelector를 활용하여 전역 변수인 theme을 선언합니다.
- styled-components의 ThemeProvider를 활용해 theme변수를 props로 전달합니다.
- 전역 스타일 선언한 GlobalStyles를 import 하여 ThemeProvider안에 컴포넌트를 추가합니다.
- useDispatch와 useCallback을 사용하여 라이트모드와 다크모드를 변경할 함수를 생성합니다.
8. 결과