Search code examples
reactjsasynchronousstateuse-effectsetstate

How to update React setState immediately


I'm calling AddItemToList() on "onClick" event. However, since setTotalExpensesAmount() is asynchronous, it's called too late and axios.post() sends incorrect value of totalExpenses to the database (previous one).

I believe, using useEffect() outside AddItemToList() function is not a correct solution.

Should I make axios.post() the callback of setTotalExpensesAmount()? If so, how can I do it?

const AddItemToList = () => {
        if (Expense !== '' && Amount !== '' && Number(Amount) > 0) {
            setExpenseAndAmountList(
                [
                    ...expenseAndAmountList, 
                    { 
                        expenseTitle: Expense,
                        expenseAmount: Amount,
                        id: Math.random() * 1000
                    }
                ]
            );

            axios.post('http://localhost:4000/app/insertedExpenseAndAmount', 
            {
                expenseTitle: Expense,
                expenseAmount: Amount
            });
            
            setTotalExpensesAmount((currentTotalExpenses: number) => currentTotalExpenses + Number(Amount));

            axios.post('http://localhost:4000/app/totalexpensesamount', 
            {
                totalExpensesAmount: totalExpenses
            });
            
            setExpense("");
            setAmount("");
            setIfNotValidInputs(false);
            setTotalBalance(Number(income) - totalExpenses);
        } else {
            setIfNotValidInputs(true);
        }
    }

Solution

  • There are 2 ways of doing this.

    1. You can calculate the newTotalExpense and do setTotalExpense with the new value. Also, pass the newTotalExpense in the post call you are making.
        const AddItemToList = () => {
            if (Expense !== '' && Amount !== '' && Number(Amount) > 0) {
                setExpenseAndAmountList(
                    [
                        ...expenseAndAmountList, 
                        { 
                            expenseTitle: Expense,
                            expenseAmount: Amount,
                            id: Math.random() * 1000
                        }
                    ]
                );
    
                axios.post('http://localhost:4000/app/insertedExpenseAndAmount', 
                {
                    expenseTitle: Expense,
                    expenseAmount: Amount
                });
    
                const newTotalExpense = currentTotalExpenses + Number(Amount)
                setTotalExpensesAmount(newTotalExpense);
    
                axios.post('http://localhost:4000/app/totalexpensesamount', 
                {
                    totalExpensesAmount: newTotalExpense
                });
                
                setExpense("");
                setAmount("");
                setIfNotValidInputs(false);
                setTotalBalance(Number(income) - totalExpenses);
            } else {
                setIfNotValidInputs(true);
            }
        }
    
    1. You can use useEffect hook which will trigger on the change of totalExpense value. You can make the post call inside the useEffect.
        useEffect(() => {
            axios.post('http://localhost:4000/app/totalexpensesamount', 
            {
                totalExpensesAmount: totalExpenses
            });        
        }, [totalExpense])
        
        const AddItemToList = () => {
            if (Expense !== '' && Amount !== '' && Number(Amount) > 0) {
                setExpenseAndAmountList(
                    [
                        ...expenseAndAmountList, 
                        { 
                            expenseTitle: Expense,
                            expenseAmount: Amount,
                            id: Math.random() * 1000
                        }
                    ]
                );
    
                axios.post('http://localhost:4000/app/insertedExpenseAndAmount', 
                {
                    expenseTitle: Expense,
                    expenseAmount: Amount
                });
                
                setTotalExpensesAmount((currentTotalExpenses: number) => currentTotalExpenses + Number(Amount));
    
                setExpense("");
                setAmount("");
                setIfNotValidInputs(false);
                setTotalBalance(Number(income) - totalExpenses);
            } else {
                setIfNotValidInputs(true);
            }
        }