Проблема в том, что useState
работает не так, как вы думаете. Внутри потребителя вы звоните api.addExpense(...)
три раза подряд синхронно с ожиданием, что вызов setState
обновит state
на месте - это не так, состояние обновляется только при следующем вызове рендеринга, а не в строке 40, как указано в console.log
, вы ожидаете этого.
Таким образом, вместо трех полных обновлений состояния, которые вы ожидаете (по одному для каждого expense
), вы получаете состояние только из последнего обновления для {id: "d6109eb5-9d7b-4cd3-8daa-ee485b08361b", name: "book", category: "personal care", value: 200}
, которое не имеет изменений и все еще основано на исходное состояние не то, где значение первого элемента было установлено равным 800.
Я бы предложил изменить ваш ExpenseState
API для массива расходов, чтобы он мог обрабатывать несколько внутренних обновлений
function addOrUpdateExpense(state, { id, name, category, value }) {
const expense = state.find(exp => exp.id === id);
if (!expense) {
return [...state, { id, name, category, value }];
} else {
const updatedExpense = state.map(exp => {
if (exp.id === expense.id) {
return {
...exp,
...{ id, name, category, value }
};
} else {
return exp;
}
});
return updatedExpense;
}
}
function addExpenses(expenses) {
let newState = [...state];
for (const expense of expenses) {
newState = addOrUpdateExpense(newState, expense);
}
setState(newState);
}
// ...usage
function doIt() {
api.addExpenses(expenses);
}
Это распространенное заблуждение, просто помните, что следующее состояние доступно только асинхронно на следующей фазе рендеринга, обрабатывайте состояние из useState
как неизменяемое в ходе рендеринга.