rz01
rz01

Reputation: 61

change website theme after using switch from component OTHER than App.js react

I am new to react. I am trying to change the entire theme of the website by using a switch that is inside a component called NavBar.js. All of the youtube videos, tutorials, and websites that I have found are basically teaching me how to script the theme change within the App.js file, but I want to change the entire theme from this file called NavBar.js because that is where the switch is.

Essentially if a user toggles the theme switch within the NavBar component, it changes the theme entirely. So far I am just able to change the background of the body, but I want to change the background on other components (as well as modify their text colors etc.). This code only changes the background of the main body. Could I just have some direction on what I would need to do to accomplish this? Could I just switch CSS style sheets?

 export default function Nav(){

   const[theme, setTheme] = useState('dark');

   const toggleTheme = () => {
    setTheme((theme) => (theme === 'dark' ? 'light' : 'dark'));

    if(theme === 'dark')
    {
        document.body.style = 'background: red;';
    }
    else{
        document.body.style = 'background: white;';
    }

   }
   
    return(
        <div className="main-container">
            <div>
                <Link to="home"><img src={logo} alt="logo" className="nav-icon"/></Link>
            </div>
            <div className='theme-Switch-container'>
            <Switch onChange={toggleTheme}/>  
            <h4 style={{position: 'relative', display: 'inline'}}>Switch Theme</h4>
            </div>

            <div>
                <NavbarOption icon={searchGlass} alt="search icon" option="Explore"/>
                <NavbarOption icon={bell} alt="bell icon" option="Notifications"/>
                <NavbarOption icon={profile} alt="profile icon" option="Profile" link="profile"/>
                <NavbarOption icon={forums} alt="forum icon" option="Forums"/>
                <NavbarOption icon={market} alt="market icon" option="Cybermarket"/>
                <NavbarOption icon={livestream} alt="livestream icon" option="Livestream"/>
                <NavbarOption icon={analytics} alt="analytics icon" option="Analytics"/>
                <NavbarOption icon={gear} alt="gear icon" option="Settings"/>
                <NavbarOption icon={help} alt="help icon" option="Help"/>
                {/* <Link to="" className="PostBtn">Post</Link> */}
                <button className="PostBtn">Post</button>
                {/* <div className="PostBtn">
                    <h3>Post</h3>
                </div> */}
            </div>
        </div>
    );
}

Upvotes: 1

Views: 1179

Answers (4)

panukettu
panukettu

Reputation: 51

If you want to avoid using context you can definitely just use a simple useEffect to toggle your root class with the theme and use CSS selectors for the component styling variations.

Example

First, a component that has:

  • State for the theme
  • Button to toggle the theme
  • useEffect which re-runs each time the theme is toggled
    • This useEffect just sets the css class of document.body to the value of theme.

const defaultTheme = "light";

const NavBar = () => {
  const [theme, setTheme] = useState(defaultTheme);
  useEffect(() => {
    document.body.setAttribute("class", theme);
  }, [theme]);

  return (
    <nav>
      <h3>Navbar</h3>
      <Button
        className="my-custom-button"
        onClick={() =>
          setTheme((prev) => (prev === "light" ? "dark" : "light"))
        }
      >
        Toggle theme ({theme})
      </Button>
    </nav>
  );
};

Our custom button component used above is defined as follows

const Button = ({
  children,
  ...props
}: PropsWithChildren<React.ButtonHTMLAttributes<HTMLButtonElement>>) => {
  // ...
  return <button {...props}>{children}</button>;
};

Using CSS for theming our custom <Button className="my-custom-button" ... />

// global button styles
.my-custom-button {
  border-radius: 5px;
  padding: 2px 5px;
}

// dark theme button styles
.dark * .my-custom-button {
  background-color: black;
  color: white;
}

// light theme button styles
.light * .my-custom-button {
  background-color: hotpink;
  color: black;
}

A codesandbox that demonstrates this in action

Upvotes: 0

Layhout
Layhout

Reputation: 1580

add a css class to the body to save the theme state like dark and in your css file(s), use body.dark selector to listen to theme changes.

const toggleTheme = () => {
  setTheme((theme) => (theme === 'dark' ? 'light' : 'dark'));
  document.body.classList.toggle("dark");
}

in css try something like:

body.dark {
  background-color: darkred;
}

body.dark .PostBtn {
  background-color: black;
  color: white;
}

Upvotes: 1

HenryDev
HenryDev

Reputation: 4953

A nice way to solve this issue will be either using redux or useContext API. I would go for useContext to keep it simple.

In your App.js you can set the context like this:

const ThemeContext = createContext(null); // keep in mind that you can set anything to creatContext as default values.

export default function MyApp() {
  return (
    <ThemeContext.Provider value="dark">
      <NavBar/>
      // add more components here that can use the default colors
    </ThemeContext.Provider>
 )
}

and then in any of your components you can consume/use the useContext like this:

function Panel({ title, children }) {
  const theme = useContext(ThemeContext);
  const className = 'panel-' + theme;
  return (
    <section className={className}>
      <h1>{title}</h1>
      {children}
    </section>
  )
}

function Button({ children }) {
  const theme = useContext(ThemeContext);
  const className = 'button-' + theme;
  return (
    <button className={className}>
      {children}
    </button>
  );
}

You can read more about useContext API from here useContext

Hope that helps!

Upvotes: 1

MOLLY
MOLLY

Reputation: 489

There is two way to do.

  1. use useContext to save the theme value and call in App.js

  2. set a state in App.js and change state in NavBar.js

Way 2 would be like:

app.js

const[theme, setTheme] = useState('dark');
...
<NavBar theme={theme} setTheme={setTheme} .../>
...

NavBar.js

function NavBar(props){

   const {theme,setTheme} = props

   const toggleTheme = () => {
    setTheme((theme) => (theme === 'dark' ? 'light' : 'dark'));

    if(theme === 'dark')
    {
        document.body.style = 'background: red;';
    }
    else{
        document.body.style = 'background: white;';
    }

   }
...

Upvotes: 0

Related Questions