coding a toggle mode using state object - reactjs

I have a problem to write code:
I have a state
const [theme, setTheme] = useState({ mode: "LIGHT" });
and I want to made a toggle function that change mode to 'DARK' and change DARK to 'LIGHT' by double click. how can I write it?
import { createContext, useContext, useState } from "react";
const DARK = "DARK";
const ThemeContext = createContext();
const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState({ mode: "LIGHT" });
const toggleThemeMode = () => {
setTheme();
console.log(theme);
};
return (
<ThemeContext.Provider value={{ theme, toggleThemeMode }}>
{children}
</ThemeContext.Provider>
);
};
const useTheme = () => useContext(ThemeContext);
export { ThemeProvider, useTheme, DARK };

A sample e.g. with onDoubleClick event , but if you mean just on single click change event name to onClick
const {useState} = React;
const App = () => {
const [theme, setTheme] = useState("Dark");
const handleClick = () =>{
setTheme(prev=> prev === "Dark" ? "Light" : "Dark")
}
return (
<div className={`theme ${theme}`}>
<h2> Double click to change the theme - current theme: {theme} </h2>
<br />
<br />
<button className={`btn ${theme}`} onDoubleClick={handleClick}>Change Theme</button>
<br />
<br />
<p>Some test data </p>
</div>
);
};
ReactDOM.createRoot(
document.getElementById("root")
).render(
<App/>
);
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
.Dark{
background: black;
color: white;
}
.Light{
background: white;
color: black;
}
.theme{
height: 100vh;
}
.btn{
border: 2px solid pink;
background: gray;
border-radius: 5px;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>

You can just use the following code:
const [theme, setTheme] = useState("LIGHT");
...
setTheme(theme === DARK ? "LIGHT" : DARK);
Nevertheless I suggest you to at least provide also a const for "LIGHT" value or even better: an enum. If you want to stick with two values (light and dark), you can also define the state as a boolean.

Related

How can I use useRef when using ScrollTigger in React?

I'm using Gsap's ScrollTigger to develop horizontal scrolling.
If a ref is passed when using Gsap's toArray, only the ref of the last element that uses the ref will be referenced. How can I pass all used refs to toArray?
Is only className used as an argument to toArray? Or is there another way to implement horizontal scrolling differently?
import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
import { useLayoutEffect, useRef } from 'react';
import styled from 'styled-components';
gsap.registerPlugin(ScrollTrigger);
const Home = () => {
const panelRef = useRef(null);
const containerRef = useRef(null);
useLayoutEffect(() => {
const sections = gsap.utils.toArray(panelRef); // If you pass a ref, only the last ref will be referenced
gsap.to(sections, {
xPercent: -100 * (sections.length - 1),
scrollTrigger: {
trigger: containerRef.current,
pin: true,
scrub: 1,
end: '+=3500',
},
});
}, []);
return (
<Container ref={containerRef}>
<Panel className="panel" ref={panelRef}>
ONE
</Panel>
<Panel className="panel" ref={panelRef}>
TWO
</Panel>
<Panel className="panel" ref={panelRef}>
THREE
</Panel>
</Container>
);
};
const Container = styled.div`
position: relative;
overscroll-behavior: none;
height: 100%;
width: max-content;
display: flex;
flex-direction: row;
`;
const Panel = styled.div`
height: 100%;
width: 100vw;
background-color: #000;
`;
export default Home;
import { useRef, useEffect } from 'react';
import { ScrollTrigger } from 'react-scroll-trigger';
function MyComponent() {
const triggerRef = useRef(null);
useEffect(() => {
const current = triggerRef.current;
current.addEventListener("enter", () => {
// do something
});
current.addEventListener("leave", () => {
// do something
});
return () => {
current.removeEventListener("enter", () => {});
current.removeEventListener("leave", () => {});
};
}, []);
return (
<div>
<ScrollTrigger ref={triggerRef}>
<MyContent />
</ScrollTrigger>
</div>
);
}

How to change the color of thumb switch react js

I have a switch that I would like to change the color of the circle to a dark gray. I've looked on the internet but I can't quite understand how to use the css rules that exist in the component documentation. Can anybody help me!? Here is my code:
const ThemeToggle = () => {
const { theme, setTheme } = useContext(ThemeContext);
const handleThemeToggle = () => {
setTheme(theme === 'light' ? 'dark' : 'light');
if (theme === 'light') {
document.body.classList.add('darkBackground');
} else {
document.body.classList.remove('darkBackground');
}
};
return <div>
<Switch
uncheckedIcon={false}
checkedIcon={false}
onColor={'#eee'}
onChange={handleThemeToggle}
checked={theme === 'light'}
/>
</div>
}
export default ThemeToggle;
Component Documentation: https://mui.com/material-ui/api/switch/
I use this switch to change the mode. So while in light mode the thumb would be grayed out. In dark mode, the thumbnail would be white
**ThemeContext:
export const ThemeContext = React.createContext({} as IThemeContext);
const App: React.FC = () => {
const storedTheme = localStorage.getItem("darkTheme");
const [theme, setTheme] = useState(storedTheme);
useEffect(() => {
localStorage.setItem("darkTheme", theme);
})
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<CssBaseline />
<BrowserRouter>
<GlobalStyle />
<AppRouter />
</BrowserRouter>
</ThemeContext.Provider>
);
};
Try to overwrite this class:
.MuiSwitch-colorSecondary.Mui-checked {
color: green; // any color you want
}
Example:
https://codesandbox.io/s/material-ui-switch-forked-2plbik?file=/src/components/SwitchContainer.jsx
I created the following codesandbox where you can see how I change the colour of the Switch component with CSS global class this website: https://mui.com/material-ui/api/switch/#css provides + your case that you just want it when its darkBackground:
.darkBackground {
background-color: #404042;
color: gray;
}
.light {
background-color: #fff;
color: #000;
}
.MuiSwitch-switchBase .MuiSwitch-thumb {
color: purple;
}
.MuiSwitch-switchBase + .MuiSwitch-track {
background-color: purple;
}
.darkBackground .MuiSwitch-switchBase.Mui-checked .MuiSwitch-thumb {
color: white;
}
.darkBackground .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track {
background-color: white;
}
I wasn't able to change the context that's why i asked you if you could share it but if you make the context work in this demo it should work as you expect:
DEMO:
https://codesandbox.io/s/charming-shannon-36bl1m?file=/src/styles.css

React GridList w/ Modal Images

Well, how to start, hi!
I'm creating some Slider with images using Material-UI GridList, and I want those images to be opened in a modal way, just to see them clearly.
I will put the code, and then explain.
import React, {useState} from "react";
import { makeStyles } from "#material-ui/core/styles";
import Modal from "#material-ui/core/Modal";
import tileData from './../../utils/tileData'
import useStylesForSlider from './../../hooks/useStylesForSlider'
import GridList from '#material-ui/core/GridList'
function getModalStyle() {
const top = 50
const left = 50
return {
top: `${top}%`,
left: `${left}%`,
transform: `translate(-${top}%, -${left}%)`
};
}
const useStyles = makeStyles((theme) => ({
paper: {
position: "absolute",
width: 400,
backgroundColor: theme.palette.background.paper,
border: "2px solid #000",
boxShadow: theme.shadows[5],
padding: theme.spacing(2, 4, 3)
}
}));
export default function SimpleModal() {
// I have this in another folder, i will put the other ones too, i'm just starting
const classesRoot = useStylesForSlider()
const classes = useStyles();
// getModalStyle is not a pure function, we roll the style only on the first render
const [modalStyle] = useState(getModalStyle);
const [open, setOpen] = useState(false);
const handleOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const body = (
<div style={modalStyle} className={classes.paper}>
</div>
);
return (
<div className={classesRoot.root}>
<GridList className={classesRoot.gridList} cols={2.5}>
{tileData.map((tile) => (<img src={tile.img} alt={tile.img} onClick={handleOpen} key={tile.img}/>))}
</GridList>
<Modal
open={open}
onClose={handleClose}>
{body}
</Modal>
</div>
);
}
I have in "tileData" an array with the images, and I map them into a tag just to put them in the slider (GridList). It works well. Now, i want to click some img, and then open it in modal window. I click it, and the modal opens, but now comes my question, how do I put the image I clicked somewhere in the "body" constant, or how do I do to do it well. I don't know if i'm explaining well, but I expect to have some good advices, i'm pretty new in React world
You could create a state for the current chosen image index (or a unique id) of your tileData array and then load the image in the body by its index (or id). Here is an example:
import React, { useState } from "react";
import { makeStyles } from "#material-ui/core/styles";
import Modal from "#material-ui/core/Modal";
import tileData from './../../utils/tileData'
import GridList from "#material-ui/core/GridList";
function getModalStyle() {
const top = 50;
const left = 50;
return {
top: `${top}%`,
left: `${left}%`,
transform: `translate(-${top}%, -${left}%)`
};
}
const useStyles = makeStyles((theme) => ({
paper: {
position: "absolute",
width: 400,
backgroundColor: theme.palette.background.paper,
border: "2px solid #000",
boxShadow: theme.shadows[5],
padding: theme.spacing(2, 4, 3)
}
}));
export default function SimpleModal() {
// I have this in another folder, i will put the other ones too, i'm just starting
//const classesRoot = useStylesForSlider();
const classes = useStyles();
// getModalStyle is not a pure function, we roll the style only on the first render
const [modalStyle] = useState(getModalStyle);
const [open, setOpen] = useState(false);
const [currentIdx, setCurrentIdx] = useState(null); // add a state for the current index
const handleOpen = (idx) => {
setCurrentIdx(idx); // set new current index
setOpen(true);
};
const handleClose = () => {
setCurrentIdx(null); // reset current index
setOpen(false);
};
const body = (
<div style={modalStyle} className={classes.paper}>
{tileData[currentIdx] && (
<img src={tileData[currentIdx].img} alt={tileData[currentIdx].img} />
)}
</div>
);
return (
<div>
<GridList cols={2.5}>
{tileData.map((tile, idx) => (
<img
src={tile.img}
alt={tile.img}
onClick={() => handleOpen(idx)}
key={tile.img}
/>
))}
</GridList>
<Modal open={open} onClose={handleClose}>
{body}
</Modal>
</div>
);
}
Live Demo

Setting responsive handlers in React then using them in scss

I have a custom Hook that detects whether the app is the mobile or desktop version. Serving up 2 versions of components works but I am at a loss on how to pass the variable to a scss file.
The code sandbox demo is here.
In my app.js I have a couple of classes that are modified based on #media (max-width: 768px) within the scss file. This would be fine if I only had 1 style but with multiple styles, I would like to find a way to set in React which style to use.
How do I use {windowSize} to pass a JS variable to a .scss file? If I used styled-component what would it look like?
import "./app.scss";
import useWindowSize from "./useWindowSize";
export default function App() {
const windowSize = useWindowSize();
return (
<div className="App">
<h1>Making the app responsive</h1>
<h2 className="TestTitle">{windowSize}</h2>
<p className="BoxWidth">Hello world</p>
</div>
);
}
Styling looks like this:
$width: 768px;
$Colour1: rgb(0, 255, 213);
.BoxWidth {
background-color: green;
#media (max-width: $width) {
background-color: lightblue;
}
}
This is how you can do it with styled-components:
const Box = styled.div` // or 'p' depending on which element you want to use
background-color: green;
// Note that you are using a 'width' prop that needs to be passed in
#media (max-width: ${({ width }) => width}) {
background-color: lightblue;
}
`;
export default function App() {
const windowSize = useWindowSize();
return (
<div className="App">
...
// You pass the window size in as the width prop
<Box width={windowSize}>Hello world</Box>
</div>
);
}
See your modified codesandbox
EDIT
We clarified the question in chat. To which this is the solution:
const commonStyles = { background: "pink", height: 100, margin: "0 auto" };
const SmallComponent = () => <div style={{ ...commonStyles, width: "100%" }} />;
const LargeComponent = () => (
<div style={{ ...commonStyles, width: "500px" }} />
);
const Box = styled.div`
color: white;
background-color: ${({ isMobile }) => (isMobile ? "green" : "lightblue")};
`;
export default function App() {
const windowSize = useWindowSize();
const isMobile = windowSize === "useMobileVersion";
return (
<div className="App">
<h1>Making the app responsive</h1>
<h2>{windowSize}</h2>
<Box isMobile={isMobile}>Hello world</Box>
{isMobile ? <SmallComponent /> : <LargeComponent />}
</div>
);
}
The original codesandbox link has been updated with this latest answer.
You can just use if else.
For example:
export default function App() {
const windowSize = useWindowSize();
const csTestTiele = windowSize <= 768 ? "TestTitleSmall" : "TestTitleNormal";
const csBoxWidth = windowSize <= 768 ? "BoxWidthSmall" : "BoxWidthNormal";
return (
<div className="App">
<h1>Making the app responsive</h1>
<h2 className={csTestTiele}>{windowSize}</h2>
<p className={csBoxWidth}>Hello world</p>
</div>
);
}
or use classnames library:
import cs from 'classnames';
export default function App() {
const windowSize = useWindowSize();
const csTestTiele = cs({
TestTieleSmall: windowSize <= 768,
TestTieleNormal: windowSize > 768,
});
const csBoxWidth = cs({
BoxWidthSmall: windowSize <= 768,
BoxWidthNormal: windowSize > 768,
});
return (
<div className="App">
<h1>Making the app responsive</h1>
<h2 className={csTestTiele}>{windowSize}</h2>
<p className={csBoxWidth}>Hello world</p>
</div>
);
}

Passing props in makeStyles keyframe animations

How to pass props fill value to makeStyles' keyframe? Do I have to specify initial states to pass the prop?
It works for the color but does not work for fillvalue.
---Child component
const useStyles = makeStyles({
progress: {
animation: '$load 3s normal forwards',
background: props => props.color,
width: '0',
},
"#keyframes load": {
"0%": { width: "0" },
"100%": { width: props => props.fillvalue}
}
});
export default function ProgressBar(props) {
const propsStyle = {color: props.color, fillvalue: props.fillvalue}
const classes = useStyles(propsStyle)
return(
<div>
<div className={classes.progress}>
</div>
</div>
);
}
---Parent
function App() {
return (
<div>
<ProgressBar color="#000" fillvalue = "60%"/>
</div>
);
}
The answer to this is - you can't (at least for now). This is a bug as of this writing (MUI latest release is v4.11.0 as of this writing) and is acknowledged by one of the MUI contributors. You can track its progress at this issue: https://github.com/mui-org/material-ui/issues/21011
You are going to have to find other means of passing those props without the use of keyframes
const useStyles = makeStyles({
progress: {
height: "10px",
background: (props) => props.color,
width: (props) => props.fillvalue,
transition: "width 3s"
}
});
function ProgressBar(props) {
const propsStyle = { color: props.color, fillvalue: props.fillvalue };
const classes = useStyles(propsStyle);
return (
<div>
<div className={classes.progress}></div>
</div>
);
}
function App() {
const [fill, setFill] = React.useState("0%");
return (
<div>
<button onClick={() => setFill("0%")}>0%</button>
<button onClick={() => setFill("60%")}>60%</button>
<ProgressBar color="#aaa" fillvalue={fill} />
</div>
);
}
ReactDOM.render(<App/>,document.getElementById("root"));
<body>
<div id="root"></div>
<script src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
<script src="https://unpkg.com/#material-ui/core#latest/umd/material-ui.development.js"></script>
<script type="text/babel">
const { makeStyles } = MaterialUI;
</script>
</body>
I see that there is no shorter way to do this, so I've discovered a really easy way to pass the props to the #keyframes inside makeStyles You just have
to create a "helper" hook in order to pass the props:
export const useAnimationStyles = ({ width, duration }: Props) => {
const classes = makeStyles({
'#keyframes animation': {
'0%': {
transform: 'translateX(0px)',
},
'100%': {
transform: `translateX(${width}px)`,
},
},
scroll: {
animation: `$animation linear infinite`,
animationDuration: `${duration}s`
},
})
return classes()
}
and you can use it as usual:
const classes = useAnimationStyles({width, duration})
...
<div className={classes.scroll}

Resources