Toggling button image in React - reactjs

I'm trying to toggle an icon in a button in react but I've tried everything and nothing works. I can change the background/text color toggling onClick but I'm missing something to change the icon and I don't know what! Any help?
Here's my code:
App.js
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { useState } from "react";
import Layout from "./layout";
import ThemeContext, { themes } from "./theme-context";
import { faSun, faMoon } from '#fortawesome/free-solid-svg-icons'
function App() {
const sun = <FontAwesomeIcon icon={faSun} />
const moon = <FontAwesomeIcon icon={faMoon} />
const [theme, setTheme] = useState(themes.dark)
const toggleTheme = () => theme === themes.dark ? setTheme(themes.light) : setTheme(themes.dark)
return (
<ThemeContext.Provider value={theme}>
<button onClick={toggleTheme}>
{sun}
</button>
<Layout />
</ThemeContext.Provider>
);
}
export default App;
theme-context.js
import React from "react"
export const themes = {
dark: {
color: "white",
background: "black",
padding: "5px",
width: "200px",
textAlign: "center",
},
light: {
color: "black",
background: "white",
padding: "5px",
width: "200px",
textAlign: "center",
}
}
const ThemeContext = React.createContext(themes.dark)
export default ThemeContext;
layout.jsx
import React, { useContext } from "react";
import ThemeContext from "./theme-context";
const Layout = () =>{
const theme = useContext(ThemeContext)
return (
<div style={theme} >
<p>This is your content</p>
</div>
)
}
export default Layout;
I have tried {themes.dark? : {sun} ? {moon}} but it didn't work.

The {themes.dark? : {sun} ? {moon}} was very close to correct (concept-wise, the syntax is not correct though). What you needed to do instead is { theme === themes.dark ? sun : moon } With your original snippet, you were just checking to see if themes.dark was truthy, which will always return true because it is an object. What you needed to do was check to see if the current theme was the same as themes.dark.
Hope this helps.

Related

dark mode toggle in material ui

I am trying to implement a dark toggle in my website. I have gotten the toggle to show up in the correct place. I am using the usestate hook to implement the toggle functionality. However, on clicking the toggle, does not change the theme. I don't understand what is going wrong over here.
Here is the code of the different components.
I have implemented a component for the toggle theme button. Here is the code for togglethemebutton.js
import React, { useState } from "react";
// ----------------------------------------------------------------------
import { Switch } from '#mui/material';
import { createTheme } from '#mui/material/styles';
// ----------------------------------------------------------------------
export default function ToggleThemeButton() {
const [darkState, setDarkState] = useState(false);
const palletType = darkState ? "dark" : "light";
const darkTheme = createTheme({
palette: {
type: palletType,
}
});
const handleThemeChange = () => {
setDarkState(!darkState);
};
return (
<Switch checked={darkState} onChange={handleThemeChange} />
);
}
I am using this component in the Navbar.js component.
import PropTypes from 'prop-types';
// material
import { alpha, styled } from '#mui/material/styles';
import { Box, Stack, AppBar, Toolbar, IconButton } from '#mui/material';
// components
import Iconify from '../../components/Iconify';
//
import Searchbar from './Searchbar';
import AccountPopover from './AccountPopover';
import LanguagePopover from './LanguagePopover';
import NotificationsPopover from './NotificationsPopover';
import ToggleThemeButton from './ToggleThemeButton';
// ----------------------------------------------------------------------
const DRAWER_WIDTH = 280;
const APPBAR_MOBILE = 64;
const APPBAR_DESKTOP = 92;
const RootStyle = styled(AppBar)(({ theme }) => ({
boxShadow: 'none',
backdropFilter: 'blur(6px)',
WebkitBackdropFilter: 'blur(6px)', // Fix on Mobile
backgroundColor: alpha(theme.palette.background.default, 0.72),
[theme.breakpoints.up('lg')]: {
width: `calc(100% - ${DRAWER_WIDTH + 1}px)`
}
}));
const ToolbarStyle = styled(Toolbar)(({ theme }) => ({
minHeight: APPBAR_MOBILE,
[theme.breakpoints.up('lg')]: {
minHeight: APPBAR_DESKTOP,
padding: theme.spacing(0, 5)
}
}));
// ----------------------------------------------------------------------
DashboardNavbar.propTypes = {
onOpenSidebar: PropTypes.func
};
export default function DashboardNavbar({ onOpenSidebar }) {
return (
<RootStyle>
<ToolbarStyle>
<IconButton
onClick={onOpenSidebar}
sx={{ mr: 1, color: 'text.primary', display: { lg: 'none' } }}
>
<Iconify icon="eva:menu-2-fill" />
</IconButton>
<Searchbar />
<Box sx={{ flexGrow: 1 }} />
<Stack direction="row" alignItems="center" spacing={{ xs: 0.5, sm: 1.5 }}>
// here is the toggle button
<ToggleThemeButton/>
<LanguagePopover />
<NotificationsPopover />
<AccountPopover />
</Stack>
</ToolbarStyle>
</RootStyle>
);
}
A posible solution may be to move the logic to change the theme from the ToggleThemButton component to the NavBar and just pass the values needed like this:
ToggleThemeButton:
export default function ToggleThemeButton({handleThemeChange, darkState}) {
return (
<Switch checked={darkState} onChange={handleThemeChange} />
);
}
Then in the NavBar component you could add a theme variable that its updated by the ToggleThemeButton, here i used a new createTheme with the pallete that has a background property just for testing (i don't know much about material ui)
Navbar:
export default function DashboardNavbar({ onOpenSidebar }) {
const [darkState, setDarkState] = useState(false);
const palletType = darkState ? "dark" : "light";
const darkTheme = createTheme({
palette: {
type: palletType,
background: {
default: "#fff"
}
}
});
const [theme, setTheme] = useState(darkTheme);
const handleThemeChange = () => {
setDarkState(!darkState);
setTheme(createTheme({
palette: {
type: palletType,
background: {
default: !darkState? "#000" : "#fff"
}
}
}))
};
return (
<RootStyle theme={theme}>
..... <ToggleThemeButton handleThemeChange={handleThemeChange} darkState={darkState} /> ....
</ToolbarStyle>
</RootStyle>
);
}

Why this customTheme is not applied to my buttons

Why this customTheme is not applied to my buttons. I'm using the MUI 5. Please tell me where I'm making the mistakes & how to resolve this.
import React from 'react';
import NavBar from './components/NavBar';
import { createTheme, ThemeProvider } from '#mui/material/styles';
import { Button } from '#mui/material';
const customTheme = createTheme({
overrides: {
MuiButton: {
outlined: {
backgroundImage: 'linear-gradient(to top, #d299c2 0%, #fef9d7 100%)',
color: 'green'
},
text: {
color: "red"
}
}
}
})
function App() {
return (
<div id='appDiv2'>
<ThemeProvider theme={customTheme}>
<NavBar />
<Button variant='outlined'> Test Button </Button>
</ThemeProvider>
<DirectionSnackbar />
</div>
);
}
export default App;
The structure of the theme object depends on the version of MaterialUI. If you use latest, the scheme should be
const theme = createTheme({
components: {
// Name of the component
MuiButton: {
styleOverrides: {
// Name of the slot
outlined: {
backgroundImage: 'linear-gradient(to top, #d299c2 0%, #fef9d7 100%)',
color: 'green'
},
text: {
color: "red"
}
},
},
},
});
https://mui.com/customization/theme-components/#global-style-overrides

Custom React Navigation Dark and Light themes not being applied to child components

Im having problems with passing custom light or dark themes from react-navigation. The new defined object items of a theme are not overwritten as shown below. My goal is to use custom dark and light themes that i can use in every component. But now only the default values are showing not the custom ones.
App.js:
import React from 'react';
import { AppearanceProvider } from 'react-native-appearance';
import Navigation from './components/Navigation.js';
const App = () => {
return (
<AppearanceProvider>
<Navigation />
</AppearanceProvider>
);
};
export default App;
Inside the Navigation component im using the navigationContainer to pass the theme prop based on light or dark theme.
Navigation.js:
import React, { useState } from 'react';
import { NavigationContainer, DefaultTheme, DarkTheme } from '#react-navigation/native';
import { createStackNavigator, HeaderBackButton } from '#react-navigation/stack';
import Login from './screens/Login.js';
const Navigation = props => {
//CUSTOM THEMES
const MyTheme = {
...DefaultTheme,
colors: {
...DefaultTheme.colors,
background: '#b91c22',
primary: 'rgb(255, 45, 85)',
},
};
const MyThemeDark = {
...DarkTheme,
colors: {
...DarkTheme.colors,
background: 'green',
primary: 'rgb(255, 45, 85)',
},
};
//STATES
const [darkApp, setDarkApp] = useState(false);
//SET THE THEME
const appTheme = darkApp ? MyThemeDark : MyTheme;
//STYLE
const headerStyle = {
header: {
display: 'flex',
alignItems: 'center',
height: 0,
},
title: {
position: 'absolute',
top: 0,
fontWeight: '900',
fontSize: 30,
color: '#fff',
alignSelf: 'center',
},
};
return (
<NavigationContainer theme={appTheme}>
<Stack.Navigator headerMode='screen'>
<Stack.Screen
name='Login'
component={Login}
options={{
headerStyle: headerStyle.header,
headerTitleStyle: headerStyle.title,
title: 'Login',
}}
/>
</Stact.Navigator>
</NavigationContainer>
);
};
export default Navigation;
Then for example i want to get the overwritten background color in a button component, so i use the useTheme option from react-navigation. But the background color is not overwritten the default one. I was expected that the background would have the color #b91c22 but instead it is showing rgb(240, 240, 240), what im i doing wrong here?
Example Button.js:
import React from 'react';
import { TouchableOpacity, Text, View } from 'react-native';
import { useColorScheme } from 'react-native-appearance';
import { useTheme } from '#react-navigation/native';
const Button = props => {
//PROPS
const {} = props;
//THEMES
const { colors } = useTheme();
let scheme = useColorScheme();
return (
<TouchableOpacity style={{ backgroundColor: colors.background }}>
<Text>Im Button</Text>
</TouchableOpacity>
);
};
export default Button;

How to change styles in useable component in ReactJS

I have following code.
This is my reusable components which name is "Blocks"
import React from "react";
import styles from "./Blocks.module.scss";
import cx from "classnames";
const Blocks = ({ newStyle }) => {
return (
<div className={cx(styles.blocksBox, { newStyle })}>
<div>
Some Text !!!
</div>
</div>
);
};
export default Blocks;
And this is styles from my "Blocks" component
.blocksBox {
display: grid;
grid-template-columns: 49% 50%;
padding: 10px 30px;
margin:20px;
column-gap: 30px;
}
Also I have another component which name is "MainBox", and I want to use "Blocks" component in "MainBox" with different styles. Below you can seethe code and new styles.
import React from "react";
import Blocks from "../Blocks";
import styles from "./MainBox.module.scss";
import cx from "classnames";
function MainBox() {
return (
<div>
<div> Some Text in Main Box </div>
<Blocks newStyle={styles.someStyle} />
</div>
);
}
export default MainBox;
.someStyle {
font-size: 50px;
color: red;
margin:30px;
}
But something was going wrong and new styles dose not applied, there is no any errors, I just write some wrong syntax, please help me resolve this problem
You can try concatinating styles like this:
import React from "react";
import styles from "./Blocks.module.scss";
import cx from "classnames";
const Blocks = ({ newStyle }) => {
return (
<div style= {[styles.blocksBox, newStyle ]}>
<div>
Some Text !!!
</div>
</div>
);
};
export default Blocks;
Example:
import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';
import Constants from 'expo-constants';
// You can import from local files
import AssetExample from './components/AssetExample';
// or any pure javascript modules available in npm
import { Card } from 'react-native-paper';
export default function App() {
return (
<View style={styles.container}>
<Box />
<Box newStyle={{ backgroundColor: 'yellow' }} />
</View>
);
}
const Box = ({ newStyle }) => {
return <View style={[styles.box, newStyle]}></View>;
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
box: {
backgroundColor: 'red',
width: 200,
height: 200,
},
});
output:
Expo Snack
I just deleted curly braces and everything started work
import React from "react";
import styles from "./Blocks.module.scss";
import cx from "classnames";
const Blocks = ({ newStyle }) => {
return (
<div className={cx(styles.blocksBox, newStyle )}>
<div>
Some Text !!!
</div>
</div>
);
};
export default Blocks;

How to use useTheme hook from Material-UI?

There is an example of use of useTheme hook on Material-UI website: https://material-ui.com/styles/advanced/#theming
And code here:
import React from 'react';
import { ThemeProvider, useTheme } from '#material-ui/styles';
function DeepChild() {
const theme = useTheme();
return <span>{`spacing ${theme.spacing}`}</span>;
}
function UseTheme() {
return (
<ThemeProvider
theme={{
spacing: '8px',
}}
>
<DeepChild />
</ThemeProvider>
);
}
export default UseTheme;
But it doesn't show how to inject the theme. When I try to inject some properties with className it doesn't do anything. Code here:
import React from "react";
import { ThemeProvider, useTheme } from "#material-ui/styles";
function DeepChild() {
const theme = useTheme();
return <div className={theme}> eldfo elo </div>;
}
function App() {
return (
<ThemeProvider
theme={{
spacing: "40px",
color: "yellow",
fontSize: "30px"
}}
>
<DeepChild />
</ThemeProvider>
);
}
export default App;
My question is how to inject it correctly?
Use it just like any other React hook:
function App() {
const theme = useTheme<Theme>()
const space = theme.spacing(1)
return <p>Space: {space}</p>
}
Note that what you get is the full theme. If you want to get any class name, you should use theme.className but you did not define a className property in your theme example.
What you want to do is probably:
import React from "react";
import { ThemeProvider, useTheme } from "#material-ui/styles";
function DeepChild() {
const theme = useTheme();
return <div className={theme.divClass}> eldfo elo </div>;
}
function App() {
return (
<ThemeProvider
theme={{
divClass: {
spacing: "40px",
color: "yellow",
fontSize: "30px"
}
}}
>
<DeepChild />
</ThemeProvider>
);
}
export default App;

Resources