Почему подсчет с помощью React Hooks useState () чередуется между двумя числами? - PullRequest
0 голосов
/ 13 февраля 2020

Ниже приведен эксперимент с использованием useState(). Он работает нормально, кроме случаев, когда нажата кнопка +, тогда число может меняться от 7001 и 7000, а затем быстро мигать между некоторыми числами.

На самом деле, без нажатия на +, число вело себя хорошо, но примерно до 8000 или 9000, тогда оно может начать набирать sh между некоторыми числами. Почему это так и как это можно исправить?

PS Первоначальный отладочный вывод заключался в следующем: кажется, Counter() вызывался несколько раз, каждый раз устанавливая таймер интервала. Таким образом, «магическим образом» кажется, что useState() запускался только один раз - по неизвестной и магической причине - или, может быть, он запускался более одного раза, но каждый раз возвращал одно и то же содержимое для какого-то магического механизма. Начальное значение 0 действительно было таким впервые. Когда это было useState(0) для будущих времен, count не было 0 ... мы бы этого не хотели, но тогда это было не так функционально (как в математической функции).

function Counter() {
    const [count, setCount] = React.useState(0);

    setInterval(() => {
        setCount(count + 1000);
    }, 1000);

    return (
        <div>
            <button onClick={() => setCount(count + 1)}> + </button>                    
            { count }
            <button onClick={() => setCount(count - 1)}> - </button> 
        </div>
    );
}

ReactDOM.render(<Counter />, document.querySelector("#root"));
button { margin: 0 1em }
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>

<div id="root"></div>

Ответы [ 2 ]

5 голосов
/ 13 февраля 2020
  1. Код в функциональном компоненте выполняется каждый раз, когда компонент перерисовывается. Таким образом, при каждом повторном рендеринге вы запускаете бесконечный таймер, который добавляет 1000 к счетчику каждую секунду
  2. Каждый раз, когда вы изменяете состояние компонента, React повторно отображает его. Это означает, что каждое выполнение setCount приводит к новому повторному рендерингу и запускается новый таймер
  3. Кроме того, setCount является асинхронным, и если вам нужно полагаться на предыдущее состояние, чтобы определить следующее, вы должны вызвать с обратный вызов, как показано в другом ответе (setCount(c => c + 1))

Что-то подобное должно работать:

import React, {useState, useRef, useEffect} from 'react';

function Counter() {
    const [count, setCount] = useState(0);
    //useRef gives us an object to store things between re-renders
    const timer = useRef();

    useEffect(() => {
      timer.current = setInterval(() => {
        setCount(count => count + 1000);
      }, 1000);

      //If we return a function, it will be called when component is dismounted
      return () => {
        clearInterval(timer.current);
      }
    }, []);

    return (
        <div>
            <button onClick={() => setCount(count => count + 1)}> + </button>                    
            { count }
            <button onClick={() => setCount(count => count - 1)}> - </button> 
        </div>
    );
}
1 голос
/ 13 февраля 2020

Не совсем уверен насчет «почему это так», но это исправлено заменой setCount(c => c + 1) на кнопках и setCount(c => c + 1000) в интервале.

Помещение 'setInterval' в эффект также гарантирует, что есть только один интервал ...

  React.useEffect(() => {  
     setInterval(() => {
        setCount(c => c + 1000);
     }, 1000);
  },[])

PS Counter() вызывается при каждом рендеринге, я думаю ... в то время как useState вызывается только один раз за монтаж при проектировании .

...