헤더에 테마 모드 (다크모드 / 라이트모드) 를 변경할 수 있는 버튼을 구현했습니다.
react-redux-toolkit
라이브러리를 활용하여 테마모드의 상태값을 전역으로 관리하였습니다.
createSlice
를 활용하였습니다.localStorage
에 boolean 값으로 isDarkMode 가 true 인지 false 인지 저장해두는 방식을 택했습니다.document.body.dataset.theme
에 접근하여 ~~// 파일 경로: src/store/modules/header.js
import { createSlice } from '@reduxjs/toolkit';
const themeModeSlice = createSlice({
name: 'themeModeReducer',
initialState: {
isDarkMode: JSON.parse(localStorage.getItem('isDarkMode')),
},
reducers: {
darkMode: state => {
localStorage.setItem('isDarkMode', true);
state.isDarkMode = true;
document.body.dataset.theme = 'dark';
},
lightMode: state => {
localStorage.setItem('isDarkMode', false);
state.isDarkMode = false;
document.body.dataset.theme = 'light';
},
},
});
export const { darkMode, lightMode } = themeModeSlice.actions;
export const themeModeReducer = themeModeSlice.reducer;
// 파일 경로: src/store/index.js
import { configureStore } from '@reduxjs/toolkit';
import { themeModeReducer } from './modules/header';
// ... 생략
const store = configureStore({
reducer: {
darkMode: themeModeReducer,
// ... 생략
},
});
export default store;
// 파일 경로: src/main.jsx
import React from 'react';
import { Provider } from 'react-redux';
import store from './store';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import ScrollTop from './components/ScrollTop';
import App from './App';
ReactDOM.createRoot(document.getElementById('root')).render(
<BrowserRouter>
<ScrollTop />
<Provider store={store}>
<App />
</Provider>
</BrowserRouter>
);
useEffect
를 활용하여 첫 렌더링시 사용자가 이전에 설정해 두었던 테마모드를 체크해줍니다.changeTheme
함수를 호출하여 redux 상태값을 업데이트 해줍니다.keyframes
문법을 활용하여 해와 달 아이콘이 변경될 때마다 180deg 회전하도록 애니메이션을 구현하였습니다.// 파일 경로: src/layout/Header/RightIcons/ThemeMode/index.jsx
import { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { darkMode, lightMode } from '../../../../store/modules/header'; // redux 모듈 파일
import { HiMoon } from 'react-icons/hi'; // 달 아이콘
import { BsFillSunFill } from 'react-icons/bs'; // 해 아이콘
import styled from 'styled-components';
const ThemeMode = () => {
const isDarkMode = useSelector(state => state.darkMode.isDarkMode);
const dispatch = useDispatch();
useEffect(() => { // 첫 렌더링시 체크
isDarkMode ? dispatch(darkMode()) : dispatch(lightMode());
}, []);
const changeTheme = e => {
e.target.className = 'theme-mode-change setting-hover';
isDarkMode ? dispatch(lightMode()) : dispatch(darkMode());
};
return (
<ThemeModeContainer className='theme-mode-container' onClick={changeTheme}>
<div className='setting-hover'>{isDarkMode ? <HiMoon /> : <BsFillSunFill />}</div>
</ThemeModeContainer>
);
};
const ThemeModeContainer = styled.div`
@keyframes themeChange {
from {
width: 10px;
height: 10px;
transform: rotate(180deg);
}
to {
width: 24px;
height: 24px;
}
}
.theme-mode-change {
svg {
animation: themeChange 0.1s linear;
}
}
`;
export default ThemeMode;