0

I have a function called confirmOrder that takes in orderData and makes an API call with that data:

const confirmOrder = () => {
    // updateTime();
    updateDate();
    // delete this
    console.log(`order data: ${JSON.stringify(orderData)}`);
    axiosWithAuth()
        .post(`/diner/${props.account.id}/confirm-order`, orderData)
        .then(res => {
            console.log(res);
        })
        .catch(err => console.log(err));
}

The orderData object has properties for both date and time:

const [orderData, setOrderData] = useState({
    date:'',
    time: '',
    truck_id: props.selectedTruck.id,
    breakdown: props.order,
    subtotal: orderSubtotal,
    tip: tipVal.tip,
    total: orderSubtotal + Number(tipVal.tip)
})

As you can see, when I run confirmOrder (through an onClick), a call is made inside that function (i.e. updateDate) that should update the values of orderData.date and orderData.time. Here is the code for that:

    const updateDate = () => {
        let date = new Date();
        let month = date.getMonth() + 1;
        if (month < 10) {
            month = '0' + month;
        };
        let calenDay = date.getDate();
        if (calenDay < 10) {
            calenDay = '0' + calenDay;
          };
        setOrderData({
            ...orderData,
            date: `${date.getFullYear()}-${month}-${calenDay}`,
            time: `${date.getHours()}:${date.getMinutes()}`
        });
    }

The problem I'm having is when I make the API call inside of confirmOrder, orderData.date and orderData.time are being sent to the backend as empty strings instead of with the updated code. How can I resolve this problem?

mhSangar
  • 457
  • 3
  • 12
Jevon Cochran
  • 1,565
  • 2
  • 12
  • 23
  • 1
    First way: in `updateDate`, compose the object and store it in a variable, then call `setOrderData(theVar);` then `return theVar;`. This way you can do `const postData = updateDate();` and simply send that using axios. The other way is to use `useEffect` with a dependency array of `[orderData]` to run the axios request. –  May 19 '20 at 19:15
  • 1
    Note that this question is essentially a duplicate of [this famous question](https://stackoverflow.com/questions/30782948/why-calling-react-setstate-method-doesnt-mutate-the-state-immediately), just the hooks variety. –  May 19 '20 at 19:17

1 Answers1

1

In React, setting state is asynchronous. When you call the state updater, all you do is schedule an update and rerender. The rest of your function will finish running and then the state will be updated and your component rerendered.

If your UI does not depend on the date and time strings, they should not be included in the state at all. You should generate those strings and then combine them with the state to send the request.

const updateDate = () => {
    let date = new Date();
    let month = date.getMonth() + 1;
    if (month < 10) {
        month = '0' + month;
    };
    let calenDay = date.getDate();
    if (calenDay < 10) {
        calenDay = '0' + calenDay;
    }
    return ({
        date: `${date.getFullYear()}-${month}-${calenDay}`,
        time: `${date.getHours()}:${date.getMinutes()}`
    });
}

const confirmOrder = () => {
    const date = updateDate();
    axiosWithAuth()
        .post(`/diner/${props.account.id}/confirm-order`, { ...date, ...orderData })
        .then(res => {
            console.log(res);
        })
        .catch(err => console.log(err));
}
Elan Hamburger
  • 2,137
  • 10
  • 14
  • Oh okay, I didn't know that. Actually, nothing the orderData object has no effect on UI at all. I was holding that state just to pass into the .post request in an organized way. – Jevon Cochran May 19 '20 at 19:24
  • 1
    State should generally be reserved for things that your UI depends on because changing the state triggers a rerender. You wouldn't want to rerender if your UI hasn't changed and you need to rerender if your UI has. – Elan Hamburger May 19 '20 at 19:27