Почему не перезаписывается стейт в setTimeout?
Почему не работает функция mathArmy, которая должна обновлять стейт recruting
import React, { useState, useEffect, useRef, useCallback } from 'react'
import question from '../../../assets/svg/question.svg';
import archerImg from '../../../assets/png/archeryArmy.png';
import './Recruting.scss'
export default function Recruting() {
const [recruting, setRecruting] = useState({
archers: 0,
infantry: 0,
horsemen: 0,
archersGarnizon: 0,
infantryGarnizon: 0,
horsemenGarnizon: 0,
archersCooldown: 2,
archersStart: '',
archersCurrentTime: '',
archersTimeRecruting: '',
archersEnd: '',
})
const [proccessBar, setProccessBar] = useState({
archers: 0,
infantry: 0,
horsemen: 0,
})
const intervalRef = useRef(null)
const timerCalculate = (digit) => {
let hours = Math.floor(digit / 3600 % 24);
let minutes = Math.floor(digit / 60 % 60);
let seconds = Math.floor(digit % 60);
return `${hours > 0 ? `${hours}часов,` : ''} ${minutes > 0 ? `${minutes}минут,` : ''} ${seconds > 0 ? `${seconds}секунд ` : ''}`
}
const onChangeRecruting = (value, name) => {
setRecruting({ ...recruting, [name]: value })
}
const handleStartRecruting = (name, timeCooldownSec, count) => {
const timeStart = Date.now()
const timeRecruting = timeCooldownSec * count
const timeEnd = timeStart + timeRecruting
setRecruting({ ...recruting, [name + `Start`]: timeStart, [name + `End`]: timeEnd, [name + `TimeRecruting`]: timeRecruting, [name + `CurrentTime`]: timeStart })
}
const mathProccesBar = useCallback(() => {
const totalTime = recruting.archersTimeRecruting
const currentTime = parseInt(((Date.now() - recruting.archersStart) / 1000).toFixed())
const proccessBarValue = currentTime / totalTime * 100
setProccessBar({ ...proccessBar, archers: proccessBarValue })
mathArmy()
if (proccessBarValue >= 100) {
setRecruting({ ...recruting, archersTimeRecruting: '' })
clearInterval(intervalRef.current)
}
},[recruting])
const mathArmy = useCallback(() => {
const timePlusCooldown = Date.now() + recruting.archersCooldown
const currentTimeArchersPlusCd = recruting.archersCurrentTime + recruting.archersCooldown
const currentTimeCd = parseInt(((timePlusCooldown - currentTimeArchersPlusCd) / 1000).toFixed())
if (currentTimeCd >= recruting.archersCooldown) {
// setRecruting({...recruting, archersGarnizon: recruting.archersGarnizon + 1, archersCurrentTime: Date.now()})
setRecruting((prev) => {
return {
...prev,
archersGarnizon: prev.archersGarnizon + 1,
archersCurrentTime: Date.now()
}
});
}
console.log(recruting.archersGarnizon)
},[recruting])
useEffect(() => {
if (recruting.archersTimeRecruting) {
intervalRef.current = setInterval(() => mathProccesBar(), 1000)
}
}, [recruting.archersTimeRecruting])
return (
<div className="recruting">
<div className="recruting__item">
<img src={archerImg} alt="archercs" />
<div className='recruting__container'>
<span>{recruting.archersGarnizon}</span>
<button type='button' className='recruting__btnTake'>Взять в армию</button>
</div>
<div className='recruting__container'>
<span>Найм лучников</span>
{recruting.archersTimeRecruting ?
<>
<div className="recruting__proccessBar">
<div className="recruting__barItem" style={{ width: proccessBar.archers + '%' }}></div>
</div>
<div className='recruting__timer'>
{timerCalculate(recruting.archersTimeRecruting)}
</div>
</>
:
<>
<span className='recruting__counter'>{recruting.archers}</span>
<input className='recruting__input' type="range" value={recruting.archers} onChange={(e) => onChangeRecruting(e.target.value, 'archers')} />
</>
}
</div>
<div className="recruting__container">
<span className='recruting__cooldown'>{recruting.archersCooldown} секунд</span>
<button type='button' className='recruting__btnRecrut' onClick={() => handleStartRecruting('archers', recruting.archersCooldown, recruting.archers)}>Нанять</button>
</div>
</div>
<div className="recruting__item">
<img src={archerImg} alt="archercs" />
<div className='recruting__container'>
<span>30</span>
<button type='button' className='recruting__btnTake'>Взять в армию</button>
</div>
<div className='recruting__container'>
<span>Найм лучников</span>
<span className='recruting__counter'>{recruting.infantry}</span>
<input className='recruting__input' type="range" value={recruting.infantry} onChange={(e) => onChangeRecruting(e.target.value, 'infantry')} />
</div>
<div className="recruting__container">
<span className='recruting__cooldown'>5 мин</span>
<button type='button' className='recruting__btnRecrut'>Нанять</button>
</div>
</div>
<div className="recruting__item">
<img src={archerImg} alt="archercs" />
<div className='recruting__container'>
<span>30</span>
<button type='button' className='recruting__btnTake'>Взять в армию</button>
</div>
<div className='recruting__container'>
<span>Найм лучников</span>
<span className='recruting__counter'>{recruting.horsemen}</span>
<input className='recruting__input' type="range" value={recruting.horsemen} onChange={(e) => onChangeRecruting(e.target.horsemen, 'horsemen')} />
</div>
<div className="recruting__container">
<span className='recruting__cooldown'>5 мин</span>
<button type='button' className='recruting__btnRecrut'>Нанять</button>
</div>
</div>
</div>
)
}
Ответы (1 шт):
Автор решения: p1uton
→ Ссылка
setState работает асинхронно
Вызовы setState являются асинхронными, поэтому не стоит рассчитывать, что this.state отобразит новое значение мгновенно после вызова setState.
https://ru.reactjs.org/docs/faq-state.html#why-is-setstate-giving-me-the-wrong-value
Поэтому console.log сразу после setRecruting не будет показывать новое значение.