Я пытаюсь написать программу, которая увеличивает счетчик, когда пользователь нажимает «Пуск», и прекращает увеличивать его, когда пользователь нажимает «Стоп».
и тогда это должно быть
ХХХ-0-ХХХ
ХХХ-1-ХХХ
ХХХ-2-ХХХ ....
процесс должен остановиться при нажатии кнопки «Стоп».
Код (который не работает):
import React from 'react'
import './App.css'
function App() {
const [counter, setCounter] = React.useState(0)
const [intervalID, setIntervalID] = React.useState(null)
const increment = () => {
const nextCounter = counter + 1
setCounter(nextCounter)
}
const onClickStart = () => {
setIntervalID(setInterval(increment, 100))
}
const onClickStop = () => {
clearInterval(intervalID)
setIntervalID(null)
}
return (
<div className = "App">
<label>XXX-{counter}-XXX</label>
<button onClick = {onClickStart}>Start</button>
<button onClick = {onClickStop}>Stop</button>
</div>
)
}
export default App
дело в том, что после этого
ХХХ-1-ХХХ
показывается, а потом все останавливается. increment вызывается, но счетчик не увеличивается.
Я просмотрел похожие вопросы, но не нашел ни одного, где setInterval вызывается нажатием кнопки. Похоже, я не понимаю чего-то очевидного. Поэтому любая помощь приветствуется.
ОБНОВЛЯТЬ:
Здесь нет ответа на мой вопрос:
Функция JavaScript On-click для запуска и остановки интервалов
это аналогичный вопрос, но он не отвечает на мой вопрос.
Ответ на мой вопрос написан Деннисом Катсом, и ответ заключается в том, чтобы исправить функцию приращения:
const increment = () => setCounter(oldCounter => oldCounter + 1);
Проблема в том, что ваша функция приращения создает замыкание над переменной counter
, а setInterval
выполняет начальную increment
функцию, где counter
всегда равно 0, каждый раз. В результате setInterval
постоянно устанавливает счетчик на 1. Подробнее о замыканиях в React можно прочитать здесь, если вы с ними не знакомы.
Тем временем, чтобы исправить ваш код, setCounter
может дополнительно использовать функцию, которая сопоставляет текущее значение (даже после обновления состояния) с тем, каким должно быть следующее значение. Таким образом, increment
на самом деле не закрывается по состоянию счетчика, а получает его динамически через setCounter
, и поэтому он может правильно увеличивать значение.
Вот рабочий пример:
function App() {
const [counter, setCounter] = React.useState(0)
const [intervalID, setIntervalID] = React.useState(null)
// here is the primary change!!
// we simply tell setCounter to add 1 to the old counter value
const increment = () => setCounter(oldCounter => oldCounter + 1);
const onClickStart = () => {
if (intervalID === null) // small fix to prevent starting multiple intervals
setIntervalID(setInterval(increment, 100));
}
const onClickStop = () => {
clearInterval(intervalID);
setIntervalID(null);
}
return (
<div className = "App">
<label>XXX-{counter}-XXX</label>
<button onClick = {onClickStart}>Start</button>
<button onClick = {onClickStop}>Stop</button>
</div>
)
}
ReactDOM.createRoot(document.getElementById("root")).render( < App / > );
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
<div id = "root"></div>
Теперь понимаю. Видимо, я должен был сначала прочитать этот medium.com/ableneo/…. Особенно «тип SetStateAction похож на перегруженный тип initialProps. Пользователь функции может установить состояние напрямую или использовать функцию с предыдущим состоянием в качестве аргумента». Так что спасибо, теперь все ясно
Спасибо - работает, и я понял в чем проблема, и почти понял как это лечится. Единственное, чего я не понимаю, так это "const increment = () => setCounter(oldCounter => oldCounter + 1);" Как это работает? increment — это функция без параметра, которая вызывает функцию setCounter(oldCounter). Но откуда взялся oldCounter?