0

I want to store "jokes" and console log a new value every time the interval runs (a get call is made) after 5 seconds. However, the value doesn't render anything after each interval. I'm unsure if jokes are being called and captured since it prints out as JOKE: []. My end goal is to create a logic using the "joke" state.

If you wish to test it yourself, https://codesandbox.io/s/asynchronous-test-mp2fq?file=/AutoComplete.js

  const [joke, setJoke] = React.useState([]);

  React.useEffect(() => {
    const interval = setInterval(() => {
      axios.get("https://api.chucknorris.io/jokes/random").then((res) => {
        setJoke(res.data.value);
        console.log("JOKE: ", joke); // <- Doesn't print every time it is called.
      });

      console.log("Every 5 seconds");
    }, 5000);

    if (joke.length !== 0) {
      clearInterval(interval);
      console.log("Returns True");
    }

    return () => clearInterval(interval);
  }, []);

enter image description here

May
  • 61
  • 1
  • 6
  • Does this answer your question? [Can't read latest state variable in setInterval (Hooks)](https://stackoverflow.com/questions/57904458/cant-read-latest-state-variable-in-setinterval-hooks) – Giorgi Moniava Sep 05 '21 at 16:29
  • The reason is Calls to setState are asynchronous - don’t rely on this.state to reflect the new value immediately after calling setState. – Asela Priyadarshana Sep 05 '21 at 16:45

2 Answers2

0

Your setJoke call is actually working. The problem is the console.log being called right after setJoke. As mentioned in the answer of this question, setState is async. You can find this issue explained in the React docs:

Calls to setState are asynchronous - don’t rely on this.state to reflect the new value immediately after calling setState. Pass an updater function instead of an object if you need to compute values based on the current state (see below for details).

You can see that joke variable is changing every 5 seconds by adding it to the JSX:

return (
    <>
    {JSON.stringify(joke)}
      <Autocomplete
        sx={{ width: 300 }}
        open={open}
        onOpen={() => {
          setOpen(true);
        }}
    ...
0

To achieve your goal you have to, for example, split your code into two useEffect calls:

  const [joke, setJoke] = React.useState([]);

  React.useEffect(() => {
    const interval = setInterval(() => {
      axios.get("https://api.chucknorris.io/jokes/random").then((res) => {
        if (res.data.value.length !== 0) {
          setJoke(res.data.value);
          clearInterval(interval);
          console.log("Returns True");
        }
      });

      console.log("Every 5 seconds");
    }, 5000);


    return () => clearInterval(interval);
  }, []);

  React.useEffect(() => {
    // here you can write any side effect that depends on joke
  }, [joke]);