ronnyrr
ronnyrr

Reputation: 1551

React hooks exhaustive-deps infinite loop with async function which uses state

I'm trying to handle an async function to insert data in a database. Unfortunately the eslint react-hooks/exhaustive-deps requires me to add my score state in the useCallback. Although when I add this my app get in an infinite loop state.

When I add eslint-disable-line on the callback requirements line my code runs perfectly fine. Why is this rule in there, and how to solve it the correct way without disabling the eslint rule?

import React, { useState, useEffect, useCallback } from 'react';
import { useSelector, useDispatch } from 'react-redux';

import { insertData } from 'ducks/data';

const App = () => {
  const dispatch = useDispatch();
  const sliderData = useSelector((state) => state.data.sliders);
  const [score, setScore] = useState(0);

  const handleData = useCallback(() => {
    dispatch(insertData(score));
  }, [dispatch]); // eslint-disable-line

  useEffect(() => {
    for (const [key, value] of Object.entries(sliderData)) {
      const sliders = Object.values(value);
      const totalSliderScore = sliders.reduce((a, b) => a + b, 0);

      setScore((prevScore) => prevScore += totalSliderScore);

      // Last slider in array
      if (key === 'lastKey') {
        handleData();
      }
    }
  }, [sliderData, handleData]);

  return ...
};

export default App;

Upvotes: 0

Views: 621

Answers (2)

Subham Dey
Subham Dey

Reputation: 172

any state variable used in a useCallback should be included in the dependency list otherwise you might get stale data of that variable.

const handleData = useCallback(() => {
dispatch(insertData(score));
}, [dispatch, score]);

and why do you need the function handleData in the dependency list of useEffect ? Isn't that causing the loop. I think also changing to the following should solve the infinite loop.

useEffect(() => {
for (const [key, value] of Object.entries(sliderData)) {
  const sliders = Object.values(value);
  const totalSliderScore = sliders.reduce((a, b) => a + b, 0);

  setScore((prevScore) => prevScore += totalSliderScore);

  // Last slider in array
  if (key === 'lastKey') {
    handleData();
  }
}
}, [sliderData]);

Upvotes: 0

Aspirin  Wang
Aspirin Wang

Reputation: 181

You can create a reference of the latest score so that it will not cause the loop

    const [score, setScore] = useState(0);
    const latestScore = useRef();

    const handleData = useCallback(() => {
    dispatch(insertData(latestScore));
    }, [dispatch,latestScore]); // eslint-disable-line

    useEffect(() => {
        ...
        latestScore.current = score
        }
    }, []);

Upvotes: 1

Related Questions