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>
);
}
Related
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.
I wanna toggle a class when hovering on a Container by changing the opacity from 0 to 1, I've used onmouseEnter and onMouseLeave Event to toggle the class, but when I console hover state I see that is changing from true to false when I hover but the class "Show" is not changing.
What do you think ?
<--Components-->
import React,{useState} from 'react';
import './MyWork.css';
import {Visibility, GitHub } from "#material-ui/icons";
const SingleProject = ({src, title}) => {
const [hover, isHover] = useState(false);
const showIcons = isHover ? "Show" : "";
return (
<div className="card-container" onMouseEnter={()=> isHover(true)} onMouseLeave={()=> isHover(false)}>
<img src={src} alt={title}/>
<h1 id="card-title">{title}</h1>
<div className={`Info ${showIcons}`}>
<div className="Icon">
<GitHub/>
</div>
<div className="Icon">
<Visibility/>
</div>
</div>
</div>
)
}
export default SingleProject;
<--- Css--->
.card-container {
height: 314px;
width: 500px;
cursor: pointer;
position : relative;
}
.Info {
position: absolute;
height: 100%;
width: 100%;
top:0;
left:0;
display:flex;
justify-content:center;
align-items: center;
background-color: rgba(0, 0, 0, 0.5);
opacity: 0;
}
.Info.Show {
opacity: 1;
}
When assigning the value to showIcons, you need to use hover instead of isHover which is the setter function for that state.
Additionally, I recommend naming the setter function setHover to avoid confusion and be more semantic. You can also add conditional Show class like this, which is more concise:
iconst SingleProject = ({src, title}) => {
const [hover, setHover] = useState(false);
return (
<div
className="card-container"
onMouseEnter={()=> setHover(true)}
onMouseLeave={()=> setHover(false)}
>
<img src={src} alt={title}/>
<h1 id="card-title">{title}</h1>
<div className={`Info ${hover ? "Show" : ""}`}>
<div className="Icon">
<GitHub/>
</div>
<div className="Icon">
<Visibility/>
</div>
</div>
</div>
)
}
export default SingleProject;
You are using the setter instead of the state itself on your condition. Change isHover with hover like below:
const showIcons = hover ? "Show" : "";
I'm building this code in when a user clicks the image, it should assign the newstyle to the style state. But whenever I click in a position of the image, it pops up an newStyle is undefined error even when I've set the variable newStyle inside the imageClick function.
import React, {useState} from 'react'
import mainImage from '../img/marioBrosMain.jpg'
import '../main.css'
import Box from '../pages/box.js'
function Main() {
const [style, setStyle] = useState();
const imageClick = (x, y) => {
const newStyle = `position: absolute,
z-index: 2,
border-radius: 2px solid red,
border: 2px solid red,
height: 50px,
padding-left: 50px,
margin-left: 1450px,
left:${x}px,
top:${y}px`
return newStyle
}
return (
<div id="frame">
<div id="mainImage" style = {style}></div>
<img src={mainImage} className="marioBros____image" onClick={e => {
imageClick(e.screenX, e.screenY);
setStyle(newStyle);
}}/>
</div>
)
}
export default Main
It looks like newStyle is not in scope in the code you have posted. Maybe something like this would work:
<img
src={mainImage}
className="marioBros____image"
onClick={e => setStyle(imageClick(e.screenX, e.screenY)) }
/>
This way you're directly setting the style with the return value from imageClick.
I think this is the optimal code.
import React, { useState } from "react"
import mainImage from "../img/marioBrosMain.jpg"
import "../main.css"
import Box from "../pages/box.js"
export default function Main() {
const [style, setStyle] = useState()
const imageClick = (x, y) => ({
position: "absolute",
zIndex: 2,
borderRadius: 2,
borderColor: "red",
borderStyle: "solid",
height: 50,
paddingLeft: 50,
marginLeft: 1450,
left: x,
top: y
})
return (
<div id="frame">
<div id="mainImage" style={{}}></div>
<img src={mainImage} className="marioBros____image" onClick={e => setStyle(imageClick(e.screenX, e.screenY))} />
</div>
)
I am new to styled Components from react and I am having trouble creating dynamically a clock with has an initial degree for minutes(minute degrees) and an initial degree for hours (hourDegrees).
This is so far what I have achieved, but I get the following message:
Keyframes.js:20 The component styled.div with the id of "..." has been created dynamically.
You may see this warning because you've called styled inside another component.
To resolve this only create new StyledComponents outside of any render method and function component.
APP CODE
function App() {
return (
<div className="App">
<main>
<div className="clock-wrap">
<section className="firstMinute">
{numbers[0].map((coord, index) => {
return (
<Clock
key={index}
hourDegrees={coord[0]}
minuteDegrees={coord[1]}
/>
);
})}
</section>
<section className="secondMinute">
{numbers[1].map((coord, index) => {
return (
<Clock
key={index}
hourDegrees={coord[0]}
minuteDegrees={coord[1]}
/>
);
})}
</section>
</div>
</main>
</div>
);
}
But I can't solve this issue as, from what I understand, I have created separated components with the info passed as props.
import styled from 'styled-components'
export default function Clock ({ minuteDegrees, hourDegrees, index}) {
const finalHourDegrees = Number(hourDegrees + 360)
const finalMinuteDegrees = Number(minuteDegrees + 360)
const StyledClock = styled.div`
width: 10vh;
`
//hour
const animationHour = keyframes`
from {
transform: rotate(${props => props.hourDegrees}deg);
}
to {
transform: rotate(${finalHourDegrees}deg);
}
`
const HourStyled = styled.div`
animation: ${animationHour} 4s ease-out infinite;
`
//minutes
const animationMinute = keyframes`
from {
transform: rotate(${props => props.minuteDegrees}deg);
}
to {
transform: rotate(${finalMinuteDegrees}deg);
}
`
const MinuteStyled = styled.div`
animation: ${animationMinute} 4s ease-out infinite;
`
return(
<StyledClock className={index}>
<HourStyled className={hourDegrees} key={index} hourDegrees={hourDegrees}/>
<MinuteStyled className={minuteDegrees} key={index} minuteDegrees={minuteDegrees}/>
</StyledClock>
)
}
Thanks a lot beforehand!
You can create StyledClock or MinuteStyled styled-components outside the target component. Also, you can send props to the styled components if needed.
UPDATED I forked your code below and updated by using a callback function for the keyframes to pass the dynamic degrees codesandbox
const StyledComponent = styled.div`
background: ${props => props.background};
`;
Clock
const StyledClock = styled.div`
width: 6vw;
`;
export default function Clock({ minuteDegrees, hourDegrees, index }) {
return (
<StyledClock className={index}>
<Hours index={index} hourDegrees={hourDegrees} />
<Minutes index={index} minuteDegrees={minuteDegrees} />
</StyledClock>
);
}
Minutes
const animationMinute = keyframes`
from {
transform: rotate(${(props) => props.minuteDegrees}deg);
}
to {
transform: rotate(${(props) => props.finalMinuteDegrees}deg);
}
`;
const MinuteStyled = styled.div`
animation: ${animationMinute} 4s ease-out infinite;
`;
export default function Minutes({ minuteDegrees, index }) {
const finalMinuteDegrees = Number(minuteDegrees + 360);
return (
<MinuteStyled
className={minuteDegrees}
key={index}
minuteDegrees={minuteDegrees}
finalMinuteDegrees={finalMinuteDegrees}
/>
);
}
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