React update values - react-hooks

I've got problem with my first react app.
I've set the interval function which counts down from 10 to 0 and after the 0 is reached the interval is cleared. At least it should work like this, but when I console log the time it's always 10 (even though it renders properly in the browser - the value is getting smaller), so it never jumps to the else statement.
What should I do to fix this problem?
const {useState} = React;
const Timer = () => {
let flag = true;
const [time, setTime] = useState(10);
const handleClick = () => {
if (flag) {
setInterval(counter, 500);
}
}
const counter = () => {
if (time > 0) {
console.log(time);
setTime(time => time - 1);
} else {
console.log('out');
clearInterval(timer);
}
}
return(
<div>
<div>{time}</div>
<button className="start" onClick={handleClick}>START</button>
</div>
)
}
ReactDOM.render(
<Timer />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>

I managed to solve this problem. Thank you guys for trying to help :) Snippet below:
const {useState} = React;
const {useEffect} = React;
const Timer = () => {
const [flag, setFlag] = useState(false);
const [time, setTime] = useState(10);
const handleClick = () => {
setFlag(!flag);
}
useEffect(() => {
function counter () {
if (time > 0) {
setTime(time => time - 1)
}
}
if (flag) {
console.log('a');
const interval = setInterval(counter, 1000)
return () => clearInterval(interval);
}
}, [flag, time]);
return(
<div>
<div>{time}</div>
<button className="start" onClick={handleClick} >START</button>
</div>
)
}
ReactDOM.render(
<Timer />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>

Related

React Hook / How to pause & continue my timer?

Hello
I am working on a timer in React to understand how Hooks works, and so far everything is ok except the start button (in my case the timer starts automatically and start button should be use with pause). I can't figure how to resolve this problem with these hooks.
const { useRef, useState, useEffect } = React;
function Minuteur() {
const intervalRef = useRef();
const [timer, setTimer] = useState(30);
useEffect(() => {
const id = setInterval(() => {
setTimer((oldTimer) => oldTimer - 1);
}, 1000);
intervalRef.current = id;
}, []);
const stopTimer = () => {
clearInterval(intervalRef.current);
};
const resetTimer = () => {
setTimer(30)
};
const playTimer = () => {
};
return (
<div>
<p>Il reste : {timer} secondes</p>
<button onClick={playTimer}> PLAY! </button>
<button onClick={stopTimer}> STOP! </button>
<button onClick={resetTimer}> RESET! </button>
</div>
);
Codepen
function Minuteur() {
// Définition de la référence
const intervalRef = useRef();
const [timer, setTimer] = useState(30);
const [timerRunning, setTimerRunning] = useState(false); // I added a state for if the timer should be running or not
useEffect(() => {
let interval = null;
if (timerRunning) { // Check if the timer is running
interval = setInterval(() => {
setTimer(timer => timer - 1);
}, 1000);
}
return () => clearInterval(interval);
}, [timerRunning]); // rerun side effect when timerRunning changes
// Fonction permettant d'arrêter le ‘timer’
const stopTimer = () => {
setTimerRunning(false) // Set running to false
};
const resetTimer = () => {
setTimer(30);
stopTimer();
};
const playTimer = () => {
setTimerRunning(true); // set running to true
};
...
}
Edit: Everything in the [] dependency array at the end of the useEffect hook is what the side effect "watches". So by adding the timerRunning to the dependency array the useEffect hook will watch for the timerRunning and when it changes, it will cause the hook to re-render. If it is an empty array then it will only ever run on the initial load. That is why your timer started on refresh.

override one value with a new value gives value undefined

To summarize what I want to do:
Update the state depending on the previous state
I have searched in vain for a solution to the above problems. Found 3 solutions, unfortunately without any success.
1)
const Form = (props) => {
const [newValue, setNewValue] = useState(0);
const submitHandler = (e) => {
e.preventDefault();
const incrementOne = {
value: setNewValue((prevState) => {
return {...prevState, newValue: newValue + 1}
})
};
console.log(incrementOne);
};
const submitHandler = (e) => {
e.preventDefault();
const incrementOne = {
value: setNewValue(newValue + 1),
};
console.log(incrementOne);
};
3
const submitHandler = (e) => {
e.preventDefault();
const incrementOne = {
value: setNewValue(prevState => prevState + 1),
};
console.log(incrementOne);
};
Thank you in advance for your time and effort
Sincerely
/ Peter
In all your examples you are creating an object with a value property. You assume that is supposed to get it's value from calling set function returned by useState. However, the result of calling this function is updating the state, and re-rendering. The function itself doesn't return anything (undefined).
const incrementOne = {
value: setNewValue((prevState) => {
return {...prevState, newValue: newValue + 1}
})
};
You should call the setNewValue function when you want to update the value. You can calculate the new state using the previous one:
setNewValue(newValue + 1);
Or use a functional update to avoid depending on the state directly:
setNewValue(prevState => prevState + 1);
Note that the new value is only available after the component re-renders.
Example:
const { useState } = React;
const Form = (props) => {
const [newValue, setNewValue] = useState(0);
const submitHandler = () => {
setNewValue(prevState => prevState + 1);
};
const incrementOne = {
value: newValue,
};
console.log(incrementOne);
return (
<div>
<div>{newValue}</div>
<button onClick={submitHandler}>Submit</button>
</div>
);
}
ReactDOM.render(
<Form />,
root
)
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<div id="root"></div>

React Hooks useState promptly Update

How can I promptly update the number state and watch console.log(number) updated value?
const [number,setNumber] = useState(0);
const minus = () => {
setNumber(number-1);
console.log(number);
}
return (
<>
<div>{number}</div>
<button onClick={minus}>-</button>
</>
)
What you are trying to do is a side-effect: print something onto the console.
This is what useEffect hook is for - it lets you perform side effects.
So here is a possible implementation:
function App() {
const [number, setNumber] = useState(0);
const minus = () => {
setNumber(number - 1);
};
useEffect(() => {
console.log(number);
}, [number]);
return (
<>
<div>{number}</div>
<button onClick={minus}>-</button>
</>
);
}
Of course, it may be an overkill solution if you are just using console.log for debugging purpose. If that's the case, #zynkn and #deepak-k's answers work just fine.
Try this
setNumber((number)=> {number-1 ; console.log(number)});
const [number,setNumber] = useState(0);
const minus = () => {
// setNumber(number-1);
// It is also work well but it is working with async.
// So you can't see the below console.log().
// console.log(number);
setNumber((prevNumber) => {
newNumber = prevNumber - 1;
console.log(newNumber);
return newNumber;
});
}
return (
<>
<div>{number}</div>
<button onClick={minus}>-</button>
</>
)

How can I use the function in Parent component when a button is clicked in child component react hooks?

I have a function that I want to run in the parent component whenever a particular button in the child component is clicked. I am using react hooks for state management.
The Button clicked is the last one in the child component, and the function I am trying to call from the parent component is onClickHandling.
Parent component:
const SearchPage = () => {
const [searchText, setSearchTerm] = useState('');
const [image, setImage] = useState([]);
const [isLoaded, setIsLoaded] = useState(false);
const [isNext, setIsNext] = useState(false);
const [nextPageIndex, setNextPageIndex] = useState(1);
const [isHidden, setIsHidden] = useState(true);
const onInputChange = (e) => {
setSearchTerm(e.target.value);
};
const getImages = () => {
fetchImages(nextPageIndex, searchText)
.then((data) => {
setImage(data.data.results);
setIsLoaded(false);
});
};
const onSubmitHandler = (e) => {
setImage([]);
e.preventDefault();
setNextPageIndex(1);
getImages();
setIsLoaded(true);
setIsHidden(false);
};
const onClickHandling = () => {
setIsNext(true);
setNextPageIndex(parseInt(nextPageIndex + 1, 10));
};
if (isNext === true) {
fetchImages(nextPageIndex, searchText)
.then((data) => {
const result = data.data.results;
setImage(image.concat(result));
setIsLoaded(false);
});
setIsNext(false);
}
return (
<React.Fragment>
<SearchBar
className="search-bar"
onSubmitHandler={onSubmitHandler}
onInputChange={onInputChange}
searchText={searchText}
/>
<div className="image-container">
{image && (
<ImageList
image={image}
isLoaded={isLoaded}
isHidden={isHidden}
onClickHandler={onClickHandling}
/>
)}
</div>
</React.Fragment>
);
};
export default SearchPage;
Child Component:
const ImageList = ({
image, isLoaded, isHidden, onClickHandling,
}) => {
const [imageIndex, setImageIndex] = useState();
const [isOpen, setIsOpen] = useState('false');
if (isLoaded) {
return (
<div className="spinner">
<ReactLoading type="spin" color="blue" />
</div>
);
}
const onClickHandler = (e) => {
setIsOpen(true);
setImageIndex(parseInt((e.target.id), 10));
};
const imgs = image.map((img, index) => (
<img
id={index}
key={img.id}
src={img.urls.small}
onClick={onClickHandler}
/>
));
if (imgs.length === 0) {
return (
<p>No images</p>
);
}
if (isOpen === true) {
return (
<Lightbox
onCloseRequest={() => setIsOpen(false)}
mainSrc={image[imageIndex].urls.regular}
onMoveNextRequest={() => setImageIndex((imageIndex + 1) % image.length)}
onMovePrevRequest={() => setImageIndex((imageIndex + image.length - 1) % image.length)}
nextSrc={image[(imageIndex + 1) % image.length].urls.regular}
prevSrc={image[(imageIndex + image.length - 1) % image.length].urls.regular}
imageTitle={image[imageIndex].alt_description}
imageCaption={`By ${image[imageIndex].user.name}`}
/>
);
}
return (
<React.Fragment>
{imgs}
{!isHidden && <Button onClick={onClickHandling}>Click me</Button> }
</React.Fragment>
);
};
export default ImageList;
There is a typo in the name used to pass the function prop from the parent component to the child component.
<ImageList
image={image}
isLoaded={isLoaded}
isHidden={isHidden}
onClickHandling={onClickHandling}// <-- Here.
/>

Double click and click on ReactJS Component

I have a ReactJS component that I want to have different behavior on a single click and on a double click.
I read this question.
<Component
onClick={this.onSingleClick}
onDoubleClick={this.onDoubleClick} />
And I tried it myself and it appears as though you cannot register both single click and double click on a ReactJS component.
I'm not sure of a good solution to this problem. I don't want to use a timer because I'm going to have 8 of these single components on my page.
Would it be a good solution to have another inner component inside this one to deal with the double click situation?
Edit:
I tried this approach but it doesn't work in the render function.
render (
let props = {};
if (doubleClick) {
props.onDoubleClick = function
} else {
props.onClick = function
}
<Component
{...props} />
);
Here is the fastest and shortest answer:
CLASS-BASED COMPONENT
class DoubleClick extends React.Component {
timer = null
onClickHandler = event => {
clearTimeout(this.timer);
if (event.detail === 1) {
this.timer = setTimeout(this.props.onClick, 200)
} else if (event.detail === 2) {
this.props.onDoubleClick()
}
}
render() {
return (
<div onClick={this.onClickHandler}>
{this.props.children}
</div>
)
}
}
FUNCTIONAL COMPONENT
const DoubleClick = ({ onClick = () => { }, onDoubleClick = () => { }, children }) => {
const timer = useRef()
const onClickHandler = event => {
clearTimeout(timer.current);
if (event.detail === 1) {
timer.current = setTimeout(onClick, 200)
} else if (event.detail === 2) {
onDoubleClick()
}
}
return (
<div onClick={onClickHandler}>
{children}
</div>
)
}
DEMO
var timer;
function onClick(event) {
clearTimeout(timer);
if (event.detail === 1) {
timer = setTimeout(() => {
console.log("SINGLE CLICK");
}, 200)
} else if (event.detail === 2) {
console.log("DOUBLE CLICK");
}
}
document.querySelector(".demo").onclick = onClick;
.demo {
padding: 20px 40px;
background-color: #eee;
user-select: none;
}
<div class="demo">
Click OR Double Click Here
</div>
I know this is an old question and i only shoot into the dark (did not test the code but i am sure enough it should work) but maybe this is of help to someone.
render() {
let clicks = [];
let timeout;
function singleClick(event) {
alert("single click");
}
function doubleClick(event) {
alert("doubleClick");
}
function clickHandler(event) {
event.preventDefault();
clicks.push(new Date().getTime());
window.clearTimeout(timeout);
timeout = window.setTimeout(() => {
if (clicks.length > 1 && clicks[clicks.length - 1] - clicks[clicks.length - 2] < 250) {
doubleClick(event.target);
} else {
singleClick(event.target);
}
}, 250);
}
return (
<a onClick={clickHandler}>
click me
</a>
);
}
I am going to test this soon and in case update or delete this answer.
The downside is without a doubt, that we have a defined "double-click speed" of 250ms, which the user needs to accomplish, so it is not a pretty solution and may prevent some persons from being able to use the double click.
Of course the single click does only work with a delay of 250ms but its not possible to do it otherwise, you have to wait for the doubleClick somehow...
All of the answers here are overcomplicated, you just need to use e.detail:
<button onClick={e => {
if (e.detail === 1) handleClick();
if (e.detail === 2) handleDoubleClick();
}}>
Click me
</button>
A simple example that I have been doing.
File: withSupportDoubleClick.js
let timer
let latestTouchTap = { time: 0, target: null }
export default function withSupportDoubleClick({ onDoubleClick = () => {}, onSingleClick = () => {} }, maxDelay = 300) {
return (event) => {
clearTimeout(timer)
const touchTap = { time: new Date().getTime(), target: event.currentTarget }
const isDoubleClick =
touchTap.target === latestTouchTap.target && touchTap.time - latestTouchTap.time < maxDelay
latestTouchTap = touchTap
timer = setTimeout(() => {
if (isDoubleClick) onDoubleClick(event)
else onSingleClick(event)
}, maxDelay)
}
}
File: YourComponent.js
import React from 'react'
import withSupportDoubleClick from './withSupportDoubleClick'
export default const YourComponent = () => {
const handleClick = withSupportDoubleClick({
onDoubleClick: (e) => {
console.log('double click', e)
},
onSingleClick: (e) => {
console.log('single click', e)
},
})
return (
<div
className="cursor-pointer"
onClick={handleClick}
onTouchStart={handleClick}
tabIndex="0"
role="button"
aria-pressed="false"
>
Your content/button...
</div>
)
}
onTouchStart start is a touch event that fires when the user touches the element.
Why do you describe these events handler inside a render function? Try this approach:
const Component = extends React.Component {
constructor(props) {
super(props);
}
handleSingleClick = () => {
console.log('single click');
}
handleDoubleClick = () => {
console.log('double click');
}
render() {
return (
<div onClick={this.handleSingleClick} onDoubleClick={this.handleDoubleClick}>
</div>
);
}
};

Resources