0

The following code displays a list of items. The list is held in a state variable list. There are two ways to add items to the list: either manually by clicking the Inc button, or by starting the timer which adds an item to the list every 1 second.

The Inc button works but the timer function does not. When examining the timer function, the state variable list does not appear to persist its new length between calls.

Why is this? Thank you for reading.

import React, { useState } from 'react';

export function Tester() {

    const [list, setList] = useState([]);

    var timer = null;


    function startTimer() {

        // Start a timer
        if (timer == null)
            timer = setTimeout(timeoutCallback, 1000);

    }

    function timeoutCallback() {

        //
        timer = null;

        //
        btnClicked();

        //
        startTimer();

    }

    function btnClicked() {

        let today = new Date();
        let strTime = today.getHours() + ':' + today.getMinutes() + ':' + today.getSeconds();

        setList([
            ...list,
            {
                name: "item " + strTime,
                id: list.length + 1
            }
        ]);

    }


    const renderedItems = list.map(
        X => {
            return <span key={X.id}>{X.id}+{X.name} </span>
        }
    );

    return (
        <>TESTER
            <button onClick={startTimer}>Timer</button>         
            <button onClick={btnClicked}>Inc</button>
            {renderedItems}
        </>
    );

}
1
  • stale closures.. Commented Apr 12, 2022 at 14:58

1 Answer 1

1

You need to provide a callback to setList that takes the previous state value of the list and computes a new value, as the value of list will be stale.

      setList((prevList) => [
          ...prevList,
          {
              name: "item " + strTime,
              id: prevList.length + 1
          }
      ]);

In addition, you need to use useRef to persist the instance of the timer between renders without triggering new rerenders when the current value of timer changes.

  const timer = useRef(null);


  function startTimer() {

      // Start a timer
      if (timer.current == null)
          timer.current = setTimeout(timeoutCallback, 1000);

  }

  function timeoutCallback() {

      //
      timer.current = null;

      //
      btnClicked();

      //
      startTimer();

  }
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.