Home > Mobile >  React state is not updating and updates with old data when new one comes
React state is not updating and updates with old data when new one comes

Time:12-02

when I use updateCorrectAnswers with useEffect below. it is not updating my correct answers. it shows just empty array. when a new set of api call comes it shows my old data. I don't understand why. console.log image that shows old data and empty data

i revisited some of stackoverflow posts about it but it mostly about useEffect but I am trying inside of useEffect. this is whole code if anyone need https://github.com/hyklops/react-starter/tree/master/quiz-app

function App() {
  const [trivias, setTrivias] = useState(null);
  const [isFetched, setIsFetched] = useState(false);
  const [correctAnswers, setCorrectAnswers] = useState([]);

  const updateCorrectAnswers = () => {
    trivias.map((trivia) => {
      setCorrectAnswers((prev) => [...prev, trivia.correct_answer]);
    });
  };

  async function dataFetch() {
    const res = await fetch(
      "https://opentdb.com/api.php?amount=5&type=multiple"
    );
    const data = await res.json();
    return setTrivias(data.results);
  }

  const shuffle = (trivia, index) => {
    const answers = [...trivia.incorrect_answers, trivia.correct_answer];
    const shuffledAnswers = getRandomAnswers(answers);

    // const shuffleArray = (answer) => [...array].sort(() => Math.random() - 0.5);
    return shuffledAnswers;
  };

  const getRandomAnswers = (answers) => {
    const newAnswers = [];

    while (newAnswers.length !== 4) {
      const getRandomIndex = Math.floor(Math.random() * 4);

      const condition = newAnswers.some(
        (item) => item === answers[getRandomIndex]
      );

      if (!condition) {
        newAnswers.push(answers[getRandomIndex]);
      }
    }

    return newAnswers;
  };

  const getTrivias = () => {
    return trivias.map((trivia, index) => {
      return <Trivia question={trivia.question} answers={shuffle(trivia)} />;
    });
  };

  useEffect(() => {
    dataFetch();
  }, []);

  useEffect(() => {
    if (!!trivias) {
      setIsFetched(true);
      updateCorrectAnswers();
      console.log({ correctAnswers });
    }
  }, [trivias]);

  if (!isFetched) {
    return <h1>loading</h1>;
  }

  return (
    <div className="App">
      <div>{getTrivias()}</div>
      <div className="check">
        <button>Check Answers</button>
      </div>
    </div>
  );
}

export default App;

I tried useEffect and it did'nt worked.

CodePudding user response:

This is because you're calling console.log in the same function as you're calling the function to set correct answers. When you're updating state, it doesn't happen synchronously in the same render, so when you're logging correctAnswers right after updateCorrectAnswers, the state is still []. But when you're changing state, a rerender is triggered, in which the state is now the updated state. To be able to see it, you can just pop a console.log somewhere inside your App like so:

function App() {
  const [trivias, setTrivias] = useState(null);
  const [isFetched, setIsFetched] = useState(false);
  const [correctAnswers, setCorrectAnswers] = useState([]);

  console.log({ correctAnswers });
  
  const updateCorrectAnswers = () => {
    trivias.map((trivia) => {
      setCorrectAnswers((prev) => [...prev, trivia.correct_answer]);
    });
  };

Or just render it on the screen:

return (
    <div className="App">
      <div>{getTrivias()}</div>
      <div>{correctAnswers}</div>
      <div className="check">
        <button>Check Answers</button>
      </div>
    </div>
  );

This article in the doc can help understand this better.

CodePudding user response:

When you call ‘updateCorrectAnswers();‘, the correctAnswers are only updated on the next render. Your console log runs in the same render, with the old results.

CodePudding user response:

The issue you are accessing the value of the same state variable correctAnswers at the level when you are updating. The updated value will be available in the next render only.

You can use a useEffect instead like below.

useEffect(() => {
    if (isFetched) {
      console.log({ correctAnswers });
    }
  }, [isFetched]);
  • Related