Reputation: 119
I'm making an attendance form where teachers can mark who's absent and not and check who was absent the previous days. I have the current date at the top and button which allows it to change the date forward and back. However, when I try to increase the value it works fine, but then when I decrease it increases by one first and then decreases after every click.
const [date, setDate] = useState()
var today = new Date();
var dd = parseInt(String(today.getDate()).padStart(2, '0'));
var mm = String(today.getMonth() + 1).padStart(2, '0'); //January is 0!
var yyyy = today.getFullYear();
today = dd + '/' + mm + '/' + yyyy;
useEffect(() => {
setDate(today)
}, [])
const[newDay, setNewDay] = useState(dd)
const changeToTomorrow = () => {
setNewDay(newDay + 1)
setDate(newDay + '/' + mm + '/' + yyyy)
console.log(date)
}
const changeToYesterday = () => {
setNewDay(newDay - 1)
setDate(newDay + '/' + mm + '/' + yyyy)
}
return (
<div className="attendance-today-container">
<h1>Daily attendance</h1>
<div className='change-date-container'>
<div className='change-date'>
<i class="fas fa-arrow-left" onClick={changeToYesterday}></i>
<p>{date}</p>
<i class="fas fa-arrow-right" onClick={changeToTomorrow}></i>
</div>
</div>
Upvotes: 2
Views: 534
Reputation: 2679
May I suggest a different approach, as well as a different organization of the code?
const { useState, useEffect, useCallback } = React;
// utility function (not part of the component)
function formatDate(date, format = 'dd/mm/yyyy') {
const addLeadingZero = num => {
return num < 10 ? `0${num}` : num.toString();
}
const dd = addLeadingZero(date.getDate());
const mm = addLeadingZero(date.getMonth() + 1);
const yyyy = date.getFullYear().toString();
return format.replace('dd', dd).replace('mm', mm).replace('yyyy', yyyy);
}
// This constant is not strictly necessary but I think it makes the code more readable
const ONE_DAY_MSEC = 86400000;
// Component
const MyComponent = () => {
const [date, setDate] = useState(new Date()); // date as a date
const [dispDate, setDispDate] = useState(formatDate(new Date())); // date as a formatted string
useEffect(() => {
setDispDate(formatDate(date))
}, [date, setDispDate]);
const handleNextDayClick = useCallback(() => {
setDate((current) => new Date(current.getTime() + ONE_DAY_MSEC))
}, [setDate]);
const handlePrevDayClick = useCallback(() => {
setDate((current) => new Date(current.getTime() - ONE_DAY_MSEC))
}, [setDate]);
return (
<div>
<div>Date: {dispDate}</div>
<button onClick={handlePrevDayClick}>prev day</button>
<button onClick={handleNextDayClick}>next day</button>
</div>
);
}
ReactDOM.render(<MyComponent />, document.getElementById('App'));
<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="App" />
Upvotes: 0
Reputation: 46
When you update a state using setNewDay(newDay + 1)
the newDay
variable is not immediately updated. So when you do setDate(newDay + '/' + mm + '/' + yyyy)
you still use the old value of newDay
to update date
.
You have initial state newDay = 10
date = 10/xx/xx
, then you click on increase button and the next code is executed.
setNewDay(10 + 1)
setDate(10 + '/' + mm + '/' + yyyy)
After react renders component with a new state you got newDay = 11
date = 10/xx/xx
on the next click to increase you would get
setNewDay(11 + 1)
setDate(11 + '/' + mm + '/' + yyyy)
So you date
variable update is always one step behind.
You should do something like
let nextDay = newDay + 1
setNewDay(nextDay)
setDate(nextDay + '/' + mm + '/' + yyyy)
in both functions
Upvotes: 2
Reputation: 668
You are trying to set two states in a row while those are asynchronous and might not give you the updated value of newDay by the time you set date.
A fast fix can be as follows (I rearrange the code a bit with spacing):
import {useState, useEffect} from "react";
export default function SomeComponent() {
const [date, setDate] = useState()
var today = new Date();
var dd = parseInt(String(today.getDate()).padStart(2, '0'));
var mm = String(today.getMonth() + 1).padStart(2, '0'); //January is 0!
var yyyy = today.getFullYear();
today = dd + '/' + mm + '/' + yyyy;
useEffect(() => {
setDate(today)
}, [])
const[newDay, setNewDay] = useState(dd)
const changeToTomorrow = () => {
const updateDay = newDay + 1
setNewDay(updateDay)
setDate(updateDay + '/' + mm + '/' + yyyy)
console.log(date)
}
const changeToYesterday = () => {
const updateDay = newDay - 1
setNewDay(updateDay)
setDate(updateDay + '/' + mm + '/' + yyyy)
}
return (
<div className="attendance-today-container">
<h1>Daily attendance</h1>
<div className='change-date-container'>
<div className='change-date'>
<i class="fas fa-arrow-left" onClick={changeToYesterday}></i>
<p>{date}</p>
<i class="fas fa-arrow-right" onClick={changeToTomorrow}></i>
</div>
</div>
</div>
);
}
Calculate the updated date first in a new variable and then set it to both states:
const changeToTomorrow = () => {
const updateDay = newDay + 1
setNewDay(updateDay)
setDate(updateDay + '/' + mm + '/' + yyyy)
console.log(date)
}
const changeToYesterday = () => {
const updateDay = newDay - 1
setNewDay(updateDay)
setDate(updateDay + '/' + mm + '/' + yyyy)
}
Upvotes: 1