Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Set useState hook in a async loop

I'm new about reactJs.

I'm trying to concatenate results in async loop, but something is wrong. setState not save correctly, when I print it I can see it's an empty array, I think because there is inside an async call.

How I can solve that? Please suggest me.

function App() {

  const [data, setData] = useState({data:[]});

  const handleChildSubmit = (data) => {
    setData(data);
  }

  return (
    <div>
      <Form onChildSubmit={handleChildSubmit} />
      <Results flights={data} />
    </div>
  );
}

const Form = ( {onChildSubmit} ) => {

    const [dati, setDati] = useState([]);

    const onFormSubmit = async (e) => {
        e.preventDefault();

        // Flights search Parameters
        const data = new FormData(e.target);
        const dateFrom = data.get('dateFrom');
        const dateTo = data.get('dateTo');
        const departureDay = data.get('day');
        const placeFrom = data.get('placeFrom');
        const placeTo = data.get('placeTo');
        let dayDeparture;
        const daysDeparture = getDayOfWeekArray(dateFrom,dateTo,departureDay);

        // Loop of Fly Search in range time
        for(let i=0; i < daysDeparture.length; i++){
            dayDeparture = daysDeparture[i];
            axios.get('https://api.skypicker.com/flights?flyFrom='+placeFrom+'&to='+placeTo+'&dateFrom='+dayDeparture+'&dateTo='+dayDeparture+'&partner=picky')
            .then(res => {

                setDati([...dati, res.data]);
                onChildSubmit(dati);

            })
            .catch(function (error) {
                console.log('error: '+error);
            })
            .finally(function () {
            });
        }

    }
like image 668
Marco Avatar asked Feb 23 '26 20:02

Marco


2 Answers

The problem is that useState here gives you the dati variable with a specific value. Then your asynchronous stuff happens, and setDati() is called multiple times, but dati is does not change until the form is re-rendered and you then call onFormSubmit again.

You have a few options.

You could add the results only once as an array.

const results = []

for(let i=0; i < daysDeparture.length; i++){
    dayDeparture = daysDeparture[i];
    const res = await axios.get('https://api.skypicker.com/flights?flyFrom='+placeFrom+'&to='+placeTo+'&dateFrom='+dayDeparture+'&dateTo='+dayDeparture+'&partner=picky');
    results.push(res.data);
}

// Add all results when all are fetched.
setDati([...dati, ...results])

Or useReducer which reliably gives you the latest version of the state, right at the time that you change it, so you don't have stale data.

// Something like...

function reducer(state, action) {
  switch(action.type) {
    case 'append':
      return [...state, action.payload]
  }
}

function YourComponent() {
  const [dati, dispatch] = useReducer(reducer, [])

  const onFormSubmit = async (e) => {
    for(let i=0; i < daysDeparture.length; i++){
        dayDeparture = daysDeparture[i];
        const res = await axios.get('https://api.skypicker.com/flights?flyFrom='+placeFrom+'&to='+placeTo+'&dateFrom='+dayDeparture+'&dateTo='+dayDeparture+'&partner=picky')
        dispatch({type: 'append', payload: res.data})
    }
  }
}
like image 99
Alex Wayne Avatar answered Feb 26 '26 08:02

Alex Wayne


useState provides a way to use the previous state when updating (just like the callback way when you are using a class component), could give that a shot

        .then(res => {
            setDati(prevDati => ([...prevDati, res.data]));
            onChildSubmit(dati);

        })
like image 28
topched Avatar answered Feb 26 '26 08:02

topched



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!