简体   繁体   中英

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 () {
            });
        }

    }

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})
    }
  }
}

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);

        })

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM