Winterest / Dark mode with redux and react hook

Implementing dark mode

I recorded my code how I implemented dark mode in Winterest.

npm install styled-components styled-theming redux react-redux

What I built

Dark mode

./theme.js

I seperated light mode and dark mode of the color values.

//./theme.js
const theme = {
  light: {
    background: '#FFFEFC',
    fontColor: '#2D2B2B',
    red: '#e60023',
    darkRed: '#ad081b',
    ...
  },
  dark: {
    background: '#2D2B2B',
    fontColor: '#fafafa',
    red: '#e60023',
    darkRed: '#ad081b',
    ...
  },
};

export default theme;

./styles/DarkThemeProvider.js

import { useSelector } from 'react-redux';
import { ThemeProvider } from 'styled-components';
import theme from './theme';

const DarkThemeProvider = ({ children }) => {
  const darkThemeEnabled = useSelector(state => state.darkMode.isDarkMode);
  return (
    <ThemeProvider theme={darkThemeEnabled ? theme.dark : theme.light}>
      {children}
    </ThemeProvider>
  );
};

export default DarkThemeProvider;

./styles/GlobalStyle.js

import { createGlobalStyle } from 'styled-components';

const GlobalStyle = createGlobalStyle`
  body {
    background-color: ${props => props.theme.background};
    font-family: 'Noto Sans KR', Arial, Helvetica, sans-serif;
  }
`;

export default GlobalStyle;

./redux/index.js

import { combineReducers } from 'redux';
import darkMode from './darkmode';

const rootReducer = combineReducers({
  darkMode,
});

export default rootReducer;

./redux/darkmode.js

const initialState = { isDarkMode: false };

//actions
const DARK_MODE = 'toggle/DARKTHEME';

export const toggleDarkTheme = () => ({ type: DARK_MODE });

export default function darkModeReducer(state = initialState, action) {
  switch (action.type) {
    case DARK_MODE:
      return { isDarkMode: !state.isDarkMode };
    default:
      return state;
  }
}

./index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import GlobalStyle from './styles/GlobalStyle';
import Routers from './Routers';
import DarkThemeProvider from './styles/DarkThemeProvider';
import store from './store';

ReactDOM.render(
  <Provider store={store}>
    <DarkThemeProvider>
      <GlobalStyle />
      <Routers />
    </DarkThemeProvider>
  </Provider>,
  document.getElementById('root')
);

./store.js

Here is magic that save your state of the darkmode in your local storage.

import { createStore } from 'redux';
import rootReducer from './redux/index';

const localStorageKey = 'theme';
const persistedTheme = localStorage.getItem(localStorageKey);

let initialState = {
  darkMode: persistedTheme ? JSON.parse(persistedTheme) : {},
};

const store = createStore(rootReducer, initialState);

store.subscribe(() => {
  const preferences = store.getState().darkMode;
  if (!preferences) return;

  localStorage.setItem(localStorageKey, JSON.stringify(preferences));
});

export default store;

./components/Nav.js Button for the dark mode

The dark mode button placed on the navigation.

import { useSelector, useDispatch } from 'react-redux';

...

const isDarkTheme = useSelector(state => state.darkMode.isDarkMode);
const dispatch = useDispatch();

const toggleDarkMode = () => {
    dispatch(toggleDarkTheme());
  };

...

<Icon onClick={toggleDarkMode}>
    {isDarkTheme ? (
      <MdWbSunny style={buttonStyle} />
    ) : (
      <MdStarOutline style={buttonStyle} />
    )}
</Icon>
...

Resource