Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

167
Views
Why does setInterval works in useEffect but not when inside functional component context (see inside if condition)?

I was building UI for a dictionary that shows a word followed by its meaning. The word switches through a setInterval. In the below code I'm setting up the setInterval initially and then on some events. Here I'm just focused on the initial setInterval.

Although I'm quite experienced in React I am still not able to grab my head around why the setInterval works when inside useEffect hook but not from the if condition.

To replicate just comment out the if condition and uncomment useEffect block.

Can someone please explain?

import React, { useState, useEffect } from 'react';
import Dictionary from './Dictionary.json';
import './App.css';

const words = Object.keys(Dictionary);
let currentInterval;

function App() {
  const [randomWord, setRandomWord] = useState(words && words[Math.round(Math.random() * words.length)]);
  const [wordGenerationDuration, setWordGenerationDuration] = useState(10000);

  function selectRandomWord() {
    setRandomWord(words[Math.round(Math.random() * (words.length - 1))]);
  }
  
  if(!currentInterval) {
    currentInterval = setInterval(selectRandomWord, wordGenerationDuration);
  }

  // useEffect(
  //   () => {
  //     currentInterval = setInterval(selectRandomWord, wordGenerationDuration);
  //   }, []
  // )


  return (
    <div className='App'>
      <div className='dictionary-wrapper'>
        <p className='dictionary-word'>
          {randomWord}
        </p>
        <p className='dictionary-meaning'>
          {Dictionary[randomWord]}
        </p>
        <p className='next-button cursor-pointer'>
          <span
            onClick={
              () => {
                clearInterval(currentInterval);
                selectRandomWord();
                currentInterval = setInterval(selectRandomWord, wordGenerationDuration);
              }
            }
          >
            Next Word
          </span>
        </p>
        <p>
          <span>Auto Switch Duration:</span>
          <input
            className='dictionary-input'
            type='number'
            placeholder={wordGenerationDuration / 1000}
            onChange={
              (evt) => {
                const wordGenerationDuration = Math.max(evt.target?.value * 1000, 10000);
                clearInterval(currentInterval);
                setWordGenerationDuration(wordGenerationDuration);
                currentInterval = setInterval(selectRandomWord, wordGenerationDuration);
              }
            }
            min='10'
          />
        </p>
      </div>
    </div>
  );
}

export default App;

about 4 years ago · Juan Pablo Isaza
1 answers
Answer question

0

Your original interval callback is a function defined within your component (selectRandomWord).

When your component re-renders, the currentInterval will be set so your interval will not be redefined. The existing interval will then refer to the previous render's version of selectRandomWord which is no longer updating the state of the current render.

What you should really do is

  1. Store the interval hook in a ref
  2. Register your interval within useEffect(), and
  3. Make sure you clear the interval in the component cleanup
function App() {
  const interval = useRef()
  const [randomWord, setRandomWord] = useState(words && words[Math.round(Math.random() * words.length)]);
  const [wordGenerationDuration, setWordGenerationDuration] = useState(10000);

  function selectRandomWord() {
    setRandomWord(words[Math.round(Math.random() * (words.length - 1))]);
  }

  useEffect(() => {
    if (!interval.current) {
      interval.current = setInterval(selectRandomWord, wordGenerationDuration)
    }

    // cleanup
    return () => {
      clearInterval(interval.current)
    }
  }, [])

You can also clear the interval at any time using

clearInterval(interval.current)

See also https://reactjs.org/docs/hooks-faq.html#is-there-something-like-instance-variables

about 4 years ago · Juan Pablo Isaza Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!