How to update state for device width using Hooks in react - reactjs

I am working on a React project, according to my scenario, a have button in my project and I have written two functions to change background color. First function will call if device width is less than or equal to 320px. Second function will call if device width is === 768px. but here the problem is when my device width is 320px when I click the button at that time the background color is changing to red here the problem comes now when I go to 768px screen then initially my button background color has to be in blue color, but it is showing red. to show button background color blue I have to update state for device size.
So someone please help me to achieve this.
This is my code
This is App.js
import React, { useState } from 'react';
import './App.css';
const App = () => {
const [backGroundColor, setBackGroundColor] = useState(null)
const [deviceSize, changeDeviceSize] = useState(window.innerWidth);
const changeBackGroundColorForMobile = () => {
if(deviceSize <= 320) {
setBackGroundColor({
backgroundColor: 'red'
})
}
}
const changeBackGroundColorForTab = () => {
if(deviceSize === 768) {
setBackGroundColor({
backgroundColor: 'green'
})
}
}
return (
<div className='container'>
<div className='row'>
<div className='col-12'>
<div className='first'>
<button onClick={() => {changeBackGroundColorForMobile(); changeBackGroundColorForTab() }} style={backGroundColor} className='btn btn-primary'>Click here</button>
</div>
</div>
</div>
</div>
)
}
export default App
If you have any questions please let me know thank you.

You're always running two functions. Don’t need that.
You’re updating the deviceSize only on the initial render. You have to update that in orientation change also.
Set the default colour always to blue.
import React, { useEffect, useState } from "react";
import "./App.css";
const App = () => {
const [backGroundColor, setBackGroundColor] = useState({
backgroundColor: "blue"
}); // Initialize bgColor with "blue"
const [deviceSize, changeDeviceSize] = useState(window.innerWidth);
useEffect(() => {
const resizeW = () => changeDeviceSize(window.innerWidth);
window.addEventListener("resize", resizeW); // Update the width on resize
return () => window.removeEventListener("resize", resizeW);
});
const changeBgColor = () => {
let bgColor = "blue";
if (deviceSize === 768) {
bgColor = "green";
} else if (deviceSize <= 320) {
bgColor = "red";
}
setBackGroundColor({
backgroundColor: bgColor
});
}; // Update the bgColor by considering the deviceSize
return (
<div className="container">
<div className="row">
<div className="col-12">
<div className="first">
<button
onClick={changeBgColor}
style={backGroundColor}
className="btn btn-primary"
>
Click here
</button>
</div>
</div>
</div>
</div>
);
};
export default App;

I would follow the previous advice to get the width and if you have lots of child components that rely on the width then I would suggest using the useContext hook so you don't have to keep passing the window data as a prop.

You can use useWindowSize() hook to get window width. And whenever width changes you can change background color by calling the functions in useEffect()
import { useState, useEffect } from "react";
// Usage
function App() {
const [backGroundColor, setBackGroundColor] = useState(null)
const { width } = useWindowSize();
useEffect(()=>{
if(width <= 320) {
changeBackGroundColorForMobile();
}
if(width === 768) {
changeBackGroundColorForTab()
}
}, [width])
const changeBackGroundColorForMobile = () => {
setBackGroundColor({
backgroundColor: 'red'
})
}
const changeBackGroundColorForTab = () => {
setBackGroundColor({
backgroundColor: 'green'
})
}
return (
<div className='container'>
<div className='row'>
<div className='col-12'>
<div className='first'>
<button style={backGroundColor} className='btn btn-primary'>Click here</button>
</div>
</div>
</div>
</div>
)
}
// Hook
function useWindowSize() {
// Initialize state with undefined width/height so server and client renders match
// Learn more here: https://joshwcomeau.com/react/the-perils-of-rehydration/
const [windowSize, setWindowSize] = useState({
width: undefined,
height: undefined,
});
useEffect(() => {
// Handler to call on window resize
function handleResize() {
// Set window width/height to state
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
}
// Add event listener
window.addEventListener("resize", handleResize);
// Call handler right away so state gets updated with initial window size
handleResize();
// Remove event listener on cleanup
return () => window.removeEventListener("resize", handleResize);
}, []); // Empty array ensures that effect is only run on mount
return windowSize;
}

You can use useEffect hook to add an event listener to window resize.
export default function App() {
const [bgClassName, setBgClassName] = useState("btn-primary");
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
function updateWidth() {
setWidth(window.innerWidth);
if(window.innerWidth === 768){
setBgClassName('btn-primary')
}
}
window.addEventListener("resize", updateWidth);
return () => window.removeEventListener("resize", updateWidth);
}, []);
const changeColor = () => {
if (window.innerWidth < 320) {
setBgClassName("btn-danger");
} else if (window.innerWidth === 768) {
setBgClassName("btn-success");
}
};
console.log(width);
return (
<div className="container">
<div className="row">
<div className="col-12">
<div className="first">
<button
onClick={() => changeColor()}
className={`btn ${bgClassName}`}
>
Click here
</button>
</div>
</div>
</div>
</div>
);
}

Related

React check if element is visible on viewport

I am trying to identify on scroll if the div is visible on viewport. I am shring the code below:
<div id="parent">
data.map(item => {
<div id={item.id}>data.title</div>
}
<div>
Now I want to get the list of divs inside of #parent which are visible on viewport on scroll.
You can install the 'react-intersection-observer' module from the npm to do the trick. Read more from here. They have the 'inView' hook that could solve your problem.
First import the hook.
import { useInView } from "react-intersection-observer";
const [ref, inView] = useInView({
/* Optional options */
triggerOnce: true,
rootMargin: '0px 0px',
})
here, we can ref our element by
<div ref={ref}></div>
and the inView returns true when it is visible on the viewport and false when it is not.
React-Intersection-Observer using with Multiple Divs
Install package npm i react-intersection-observer
Create new file MultipleObserver.js
// MultipleObserver.js
import { useInView } from 'react-intersection-observer';
const MultipleObserver = ( {children} ) => {
const { ref, inView } = useInView({ triggerOnce: true });
return (
<div ref={ref}>
{ inView ? <span>{children}</span> : 'Loading...' }
</div>
)
}
export default MultipleObserver;
Now you can use multiple divs in the same components see example MyComponent.js
// MyComponent.js
import React from 'react';
import MultipleObserver from "MultipleObserver.js";
const MyComponent = ( props ) => {
return (
<div className="main">
<h2>Page Content</h2>
<MultipleObserver>
<img src="large-image.png" />
</MultipleObserver>
<MultipleObserver>
<iframe src="domain.com/large-video.mp4"></iframe>
</MultipleObserver>
<p>Another content</p>
</div>
}
export default MyComponent
This helped me:
<div id="parent">
data.map(item => {
<div id={`id_${item.id}`}>data.title</div>
}
<div>
and
const displayIds = (target) => {
console.log(target.replace("id_", ""));
}
const myScrollHandler = debounce(() => {
data.map(
(item: any) => {
const target =
document.querySelector(`#id_${item.id}`);
const top = target?.getBoundingClientRect().top || 0;
top >= 0 && top <= window.innerHeight ? displayIds(target);
}
);
}, 500);
useEffect(() => {
document.addEventListener("scroll", myScrollHandler);
return () => document.removeEventListener("scroll", myScrollHandler);
}, [data]);
Now for every scroll I have list of id's associated with div's which is visible on viewport.

Using css property "transition: all", I add a dynamic class: (<div className={`${show ? "show" : "hide"}></div> ), but my animation doesn't work

I am trying to make a Modal component. I would like that when the modal appears, it appears with a transition just like when it disappears. This is currently very jerky, why and how can I fix it?
I would like that when the modal is shown it is shown with an animation and the same behavior when the modal disappears (click on the button).
thank you very much for the help, I know I will learn a lot.
//content of styles.css
.modal {
position: absolute;
width: 100%;
height: 100%;
background: red;
transition: all 300ms ease-out;
transform: translateY(100%);
}
.show {
transform: translateY(0%);
}
.hide {
transform: translateY(100%);
}
app.js
import Modal from "./modal";
/*
const Modal = ({ show, children }) => {
return <div className={`modal ${show ? "show" : "hide"}`}>.
{children} </div>;
};
export default Modal;
*/
import ModalContent from "./modalContent";
/*
const ModalContent = ({ show }) => {
const showModal = () => {
show();
};
return <button onClick={showModal}> close modal</button>;
};
export default ModalContent;
*/
export default function App() {
const [show, setShow] = useState(false);
const closeModal = () => {
setShow(false);
};
useEffect(() => setShow(true), []);
return (
<div className="App">
{show && (
<Modal show={show}>
<ModalContent show={closeModal} />
</Modal>
)}
</div>
);
}
I updated my code:
this is my live code
First of all, in your demo modal disappears immediately, without any transition. It seems, that it's cause by re-rendering of whole App component, on show state change. Extracting Modal component out of App do the trick for me:
const Modal = ({ show, children }) => {
useEffect(() => {}, [show]);
return <div className={`modal ${show ? "show" : "hide"}`}>{children} </div>;
};
export default function App() {
Second point - you can't control initial setup just with with css transition. Transition appears when something (class, attribute, pseudoclass) changes on the given element. To get around this and have smooth modal appearance, you can setup one-time useEffect in the App component, which will change show state from false to true. My overall snippet:
const Modal = ({ show, children }) => {
return <div className={`modal ${show ? "show" : "hide"}`}>{children} </div>;
};
export default function App() {
const ModalContent = ({ show }) => {
const showModal = () => {
show();
};
return <button onClick={showModal}> close modal</button>;
};
const [show, setShow] = useState(false);
const closeModal = () => {
setShow(false);
};
useEffect(() => setShow(true), [])
return (
<div className="App">
<Modal show={show}>
<ModalContent show={closeModal} />
</Modal>
</div>
);
}

How to add multiple events in one tag?

I'm making a counting timer which is described below with this react functional component
import {useEffect, useState, useRef} from 'react'
function Content() {
const [countdown, setCountdown] = useState(10)
const [show, setShow] = useState(true)
const ref = useRef()
function handleStart() {
ref.current = setInterval(() => {
setCountdown(prev => prev - 1)
}, 1000)
}
function handleStop() {
clearInterval(ref.current)
}
return (
<div>
<h2 style={{padding: 20}}>Time remaining: {countdown}</h2>
<button onClick={handleStart}>Start</button>
<button onClick={handleStop}>Stop</button>
</div>
)
}
export default Content;
How do I hide these two buttons after clicking one of the two.
Assuming show is the variable to control whether the buttons are visible or not.
<div>
<h2 style={{padding: 20}}>Time remaining: {countdown}</h2>
{show && <>
<button onClick={() => {
setShow(false)
handleStart()
}}>Start</button>
<button onClick={() => {
setShow(false)
handleStop()
}}>Stop</button>
</>}
</div>
React children need to return one element, so you can either wrap it in a div, or an empty element, <> </>, so you can return multiple nodes without adding a div, span, etc.
show && <></> means if show is true, the right-hand side will render, otherwise, it won't be rendered.
First, you have to introduce new state variable, you need one ror the start btn and another for the stop btn.
You have to setShow to false on either click and render the buttons conditionally depending on show variable:
const [countdown, setCountdown] = useState(10)
const [showStart, setShowStart] = useState(true)
const [showStop, setShowStop] = useState(true);
const ref = useRef()
function handleStart() {
setShowStart(false);
ref.current = setInterval(() => {
setCountdown(prev => prev - 1)
}, 1000)
}
function handleStop() {
setShowStop(false);
clearInterval(ref.current)
}
return (
<div>
<h2 style={{padding: 20}}>Time remaining: {countdown}</h2>
{showStart && <button onClick={handleStart}>Start</button>}
{showStop && <button onClick={handleStop}>Stop</button>}
</div>
)
Hope the Below Code Solver Your Problem
import React, { useEffect, useState, useRef } from 'react';
function Example() {
const [countdown, setCountdown] = useState(10);
const [show, setShow] = useState(true);
const ref = useRef();
function handleStart() {
setShow(!show);
ref.current = setInterval(() => {
setCountdown((prev) => prev - 1);
}, 1000);
}
function handleStop() {
setShow(!show);
clearInterval(ref.current);
}
return (
<div>
<h2 style={{ padding: 20 }}>Time remaining: {countdown}</h2>
{show && (
<div>
<button onClick={handleStart}>Start</button>
<button onClick={handleStop}>Stop</button>
</div>
)}
</div>
);
}
export default Example;

Local storage and persistent state in React

I want to make a like button where user can click and like something. When user clicks the button remains red even after refresh. How can i implement this?
I have this code. When i refresh the local storage gets reset. How can i get around this?
useEffect(() => {
setColor(window.localStorage.getItem('color'));
}, []);
useEffect(() => {
window.localStorage.setItem('color', color);
}, [color]);
const handleClick = () => {
setClicked(prevValue => !prevValue)
if(clicked){
setColor("red")
}else{
setColor("")
}
}
<div className="App">
<div className="container">
<button style={{backgroundColor: color}} onClick={handleClick} > +</button>
</div>
</div>
Try this approach. We need check twice localStorage first when the component mounting, second when we clicked the button. example
App.js
import { useState, useEffect } from "react";
const App = () => {
const [color, setColor] = useState("");
useEffect(() => {
const lS = window.localStorage.getItem("color");
if (lS) return setColor(lS);
localStorage.setItem("color", "");
}, []);
const handleClick = () => {
const lS = window.localStorage.getItem("color");
if (lS === "") {
localStorage.setItem("color", "red");
setColor("red");
}
if (lS !== "") {
localStorage.setItem("color", "");
setColor("");
}
};
return (
<div className="App">
<div className="container">
<button
style={{ backgroundColor: color }}
className="like-button"
onClick={handleClick}
>
+
</button>
</div>
</div>
);
};
export default App;
I have tried to duplicate this error in a sandbox. However, on my machine it works. Could it be that you have localStorage.removeItem('color') somewhere else in your project and gets called? Or maybe a problem with your browser. Here is the sandbox where it works: https://codesandbox.io/s/magical-shannon-cot7i?file=/src/App.js
I hope, it will work I have not tested it but I am sure it should work
useEffect(() => {
const storedColor = localStorage.getItem('color')
if(storedColor) {
setColor(storedColor);
}
}, []);
const handleClick = () => {
setClicked(prevValue => !prevValue)
if(clicked){
setColor("red");
localStorage.setItem('color', color);
}else{
setColor("")
}
}
return <div className="App">
<div className="container">
<button style={{backgroundColor: color}} onClick={handleClick} > + </button>
</div>
</div>

How to use useState hook for a randomizing array problem(React useState)?

I'm trying to figure out how to solve a color boxes exercise that applies useState hook concept. Given an array that contains 12 different unique colors, initially the state will show 12 divs with a corresponding color. Upon clicking the button, only randomly chosen div will change to a random color(out of the 12 given colors) as well as flagging that div with the message "changed" on the div. So far I was able to make the color box container showing each color on a div. I see the state is changing to a random color every time when I click. But I don't know how to make only that random div to change the color and show the message. Does this problem require a unique id for each color for tracking change of the state?
import React, { useState } from 'react';
import ColorBox from './ColorBox';
import {choice} from './colorHelpers';
const ColorBoxes = () => {
const [ boxes, setBoxes] = useState(colors);
const [msg, setMsg] = useState(null);
const clickHandler = () => {
setBoxes(()=>choice(colors));
setMsg('changed');
};
return (
<>
{colors.map((color,i) =>{
return(
<div>
<ColorBox key={i} color={color} />{color}
</div>
);
})}
<button onClick={clickHandler}>Change Color!</button>
</>
);
};
import React from 'react';
import './ColorBox.css';
const ColorBox = ({ color }) => {
return <div className="colorBox" style={{ backgroundColor: color }} />;
};
export default ColorBox;
const choice = (arr) => {
const randIdx = Math.floor(Math.random() * arr.length);
return arr[randIdx];
};
export { choice };
You should save your initial color inside useState , then change color for random index with useState, check this example:
const colors = [
"#8391B5",
"#290D11",
"#0C9ABC",
"#0E17F4",
"#97BC89",
"#6B48F7",
"#584A35",
"#669F15",
"#15FC93",
"#7C8329",
"#27D792",
"#4786C8",
];
const ColorBoxes = () => {
const [boxes, setBoxes] = React.useState(
colors.sort(() => Math.random() - 0.5)
);
const [msg, setMsg] = React.useState(Array.from(Array(12)));
const clickHandler = (index) => {
const randomColor = colors[Math.floor(Math.random() * 12)];
setBoxes((prev) => prev.map((x, i) => (i === index ? randomColor : x)));
setMsg((prev) => prev.map((x, i) => (i === index ? "changed!" : x)));
};
return (
<React.Fragment>
{boxes.map((color, i) => (
<ColorBox key={i + color} color={color} msg={msg[i]} />
))}
<button onClick={() => clickHandler(Math.floor(Math.random() * 12))}>
Change Color!
</button>
</React.Fragment>
);
}
const ColorBox = ({ color, msg }) => (
<div className="colorBox" style={{ backgroundColor: color }}>
{color} {msg}
</div>
);
ReactDOM.render(
<ColorBoxes />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>

Resources