Home > Software engineering >  React, implementing Dark-Light-Mode with localStrorage
React, implementing Dark-Light-Mode with localStrorage

Time:03-24

I'm trying to use use-local-storage to achieve a theme changer in React.

App component:

import './App.css';
import React from 'react';
import { Navbar, SearchBar, Header, Main, Chart, Map } from './components';
import { Routes, Route, BrowserRouter } from 'react-router-dom';
import useLocalStorage from 'use-local-storage';

function App() {

  //  a function that toggles between darkmode and lightmode in css
  const defaultDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
  const [theme, setTheme] = useLocalStorage('theme', defaultDark ? 'dark' : 'light');
  const switchTheme = () => {
    const newTheme = theme === 'light' ? 'dark' : 'light';
    setTheme(newTheme);
  }
  console.log(theme)

  return (
    <BrowserRouter>
      <div className='App' data-theme={theme} >
        <Header />
        <SearchBar />
        <Navbar switchTheme={switchTheme} />
        <Routes>
          <Route path="/" element={<Main />} />
          <Route path="/map" element={<Map />} />
          <Route path="/chart" element={<Chart />} />
        </Routes>
      </div>
    </BrowserRouter>
  );
}

export default App;

Navbar component:

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faMapLocationDot, faChartLine, faHouseUser } from '@fortawesome/free-solid-svg-icons'
import React from 'react'
import { Link } from 'react-router-dom'


const Navbar = ({switchTheme}) => {
  return (
    <nav className='nav'>
      <button onClick={switchTheme}>Toggle</button>
      <Link to='/'>
        <FontAwesomeIcon icon={faHouseUser} size='4x' color='blue' />
        <br></br>
        Home
      </Link>
      <Link to='/map'>
        <FontAwesomeIcon icon={faMapLocationDot} size='4x' />
        <br></br>
        Map</Link>
      <Link to='/chart'>
        <FontAwesomeIcon icon={faChartLine} size='4x' color='red' />
        <br></br>
        Chart</Link>

    </nav>
  )
}

export default Navbar

CSS:

*, *::after, *::before {
box-sizing: border-box;
margin: 0;
padding: 0;
}

/****************** VARIABLES ******************/

  :root {
    --background-color:coral;
  }
  
  [data-theme="light"] {
    --background-color:red;
  }
  
  [data-theme="dark"] {
    --background-color:yellow;
  }



body {
background-color:var(--background-color);
font-family: 'Roboto', sans-serif;
font-size: 16px;
color: #333;
line-height: 1.5;
margin: 2vmin;
}

.App {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}


/**********************  SearchBar  **********************/

form {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
max-width: 500px;
margin: 0 auto;
}

form > svg {
margin-left: -20px;
}

input {
font-size:inherit;
border-radius: 1vmin;
border: .5px solid #ccc;
padding: .5rem;
}

input:focus {
border-color: #333;
}



nav {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
padding: 0.5rem;
background-color: yellow;
border-bottom: 1px solid #eaeaea;
width: 10vw;
height: 50vh;
border: 3px dotted purple;
align-self: flex-start;
}

a {
text-decoration: none;
}




/* a:active  {
/* do sth with selected Link 
} */

I am getting the correct values from console.log(theme) in App.js but I can't change the background colour of the whole app. Any ideas to solve this issue ?

CodePudding user response:

You are having a cascading issue. You are setting your theme colors on body, and trying to change it later trough App. You need to add the data-them on body itself or on html, witch comes before, not on something that comes after.

Adding this useEffect in App.js just before your return would work :

  useEffect(() => {
    document.documentElement.setAttribute("data-theme", theme);
  }, [theme]);

You can test it here on CodeSandbox. And here is your hole App component:

import './App.css';
import React, {useEffect} from 'react';
import { Navbar, SearchBar, Header, Main, Chart, Map } from './components';
import { Routes, Route, BrowserRouter } from 'react-router-dom';
import useLocalStorage from 'use-local-storage';

function App() {

  //  a function that toggles between darkmode and lightmode in css
  const defaultDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
  const [theme, setTheme] = useLocalStorage('theme', defaultDark ? 'dark' : 'light');
  const switchTheme = () => {
    const newTheme = theme === 'light' ? 'dark' : 'light';
    setTheme(newTheme);
  }

  useEffect(() => {
    document.documentElement.setAttribute("data-theme", theme);
  }, [theme]);
  return (
    <BrowserRouter>
      <div className='App'>
        <Header />
        <SearchBar />
        <Navbar switchTheme={switchTheme} />
        <Routes>
          <Route path="/" element={<Main />} />
          <Route path="/map" element={<Map />} />
          <Route path="/chart" element={<Chart />} />
        </Routes>
      </div>
    </BrowserRouter>
  );
}

export default App;
  • Related