Im new to react and trying to add darkmode/lightmode switch to my site. I have done this succesfully. However, the switch button to change between dark and light mode only works in app.tsx, this is due to the function only works in app.tsx.
So to my question, how do i pass a function to a component in react? i have tried props etc. but none have worked.
Here is my app.tsx
import React, { useState } from "react";
import logo from "./assets/logo.png";
import "./App.css";
import Navbar from "./components/Navbar/Navbar";
import "bootstrap/dist/css/bootstrap.min.css";
import { ThemeProvider } from "styled-components";
import { lightTheme, darkTheme } from "./theme";
import { GlobalStyles } from "./global";
function App() {
const [theme, setTheme] = useState("light");
function toggleTheme() {
if (theme === "light") {
setTheme("dark");
} else {
setTheme("light");
}
};
return (
<ThemeProvider theme={theme === "light" ? lightTheme : darkTheme}>
<GlobalStyles />
<div className="App">
<Navbar/>
<button onClick={toggleTheme}></button>
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1>linusromland.com</h1>
</div>
</div>
</ThemeProvider>
);
}
export default App;
and here is my Navbar.tsx
import React, { useState } from "react";
import BSNavbar from "react-bootstrap/Navbar";
import Container from "react-bootstrap/Container";
import Nav from "react-bootstrap/Nav";
import Button from "react-bootstrap/Button";
import "./Navbar.css";
import "bootstrap/dist/css/bootstrap.min.css";
import lightmode from "../../assets/lightmode.svg";
import darkmode from "../../assets/darkmode.svg";
class Navbar extends React.Component {
render() {
return (
<div className="Navbar" data-testid="Navbar">
<BSNavbar bg="light" expand="lg">
<Container className="col-5">
<BSNavbar.Brand href="/">Linus Romland</BSNavbar.Brand>
<BSNavbar.Toggle />
<BSNavbar.Collapse className="justify-content-end">
<Nav>
<Nav.Link href="/" className="text-dark">
Home
</Nav.Link>
<Nav.Link
href="https://github.com/linusromland/"
className="text-dark"
>
GitHub
</Nav.Link>
<Nav.Link href="/about" className="text-dark">
About
</Nav.Link>
<Button variant="light">
<img src={lightmode} alt="Darkmode/Lightmode change" />
</Button>
</Nav>
</BSNavbar.Collapse>
</Container>
</BSNavbar>
</div>
);
}
}
export default Navbar;
Thank you!
CodePudding user response:
You can provide a callback function to your NavBar component and the function will get the App.tsx context
<Navbar toggleTheme={toggleTheme} />
then in your component
<Button variant="light" onClick={this.props.toggleTheme}>
<img src={lightmode} alt="Darkmode/Lightmode change" />
</Button>
CodePudding user response:
A good use case for provider pattern. Basically, you would create a so-called "provider" component with an internal state to encapsulate your current theme and a function for toggling the theme:
import React, { useContext, createContext, useState, useMemo } from 'react'
const SelectThemeContext = createContext()
const SelectThemeProvider = ({ children }) => {
const [theme, setTheme] = useState('light');
const toggleTheme = () => if (theme === "light") {
setTheme("dark");
} else {
setTheme("light");
}
const value = useMemo(() => [theme, toggleTheme], [theme, toggleTheme]);
return <SelectThemeContext.Provider value={value}>{children}</SelectThemeContext.Provider>
}
const useSelectThemeContext = () => useContext(WordsContext);
export { useSelectThemeContext, SelectThemeProvider }
Fix your App.tsx to something like this (wrap the content with SelectThemeProvider
):
import { useSelectThemeContext } from './SelectThemeProvider';
const App = () => {
const [theme] = useSelectThemeContext();
return (
<SelectThemeProvider>
<ThemeProvider theme={theme}>
<GlobalStyles />
<div className="App">
<Navbar/>
<button onClick={toggleTheme}></button>
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1>linusromland.com</h1>
</div>
</div>
</ThemeProvider>
</SelectThemeProvider>
);
}
To switch theme in Navbar:
import { useSelectThemeContext } from './SelectThemeProvider';
class Navbar extends React.Component {
const [,toggleTheme] = useSelectThemeContext();
render() {
return (
<div className="Navbar" data-testid="Navbar">
<BSNavbar bg="light" expand="lg">
<Container className="col-5">
<BSNavbar.Brand href="/">Linus Romland</BSNavbar.Brand>
<BSNavbar.Toggle />
<BSNavbar.Collapse className="justify-content-end">
<Nav>
<Nav.Link href="/" className="text-dark">
Home
</Nav.Link>
<Nav.Link
href="https://github.com/linusromland/"
className="text-dark"
>
GitHub
</Nav.Link>
<Nav.Link href="/about" className="text-dark">
About
</Nav.Link>
// Here! Just pass toggleTheme function to onClick
<Button variant="light" onClick={toggleTheme}>
<img src={lightmode} alt="Darkmode/Lightmode change" />
</Button>
</Nav>
</BSNavbar.Collapse>
</Container>
</BSNavbar>
</div>
);
}
}
Point of the story: You don't have to pass your function for theme toggling down the component tree. Simply import useSelectThemeContext
in any component which is wrapped by SelectThemeProvider
and use toggleTheme
function to toggle the theme.