I'm now learning React and I have a problem with re-rendering component. App.js code:
function App() {
const [expenses, setExpenses] = useState(INITIAL_EXPENSES);
const addNewExpenseHandler = (expense) => {
setExpenses((prevState) => {
return [expense, ...prevState];
}, changeYearHandler(filteredYear));
};
const filterExpenses = (expenses, year) => {
const newFilteredExpenses = expenses.filter((expense) => {
if (String(expense.date.getFullYear()) === year) {
return expense;
}
});
return newFilteredExpenses;
};
const [filteredYear, setFilteredYear] = useState('2019');
const [filteredExpenses, setFilteredExpenses] = useState(
filterExpenses(expenses, filteredYear)
);
const changeYearHandler = (value) => {
setFilteredYear(
value,
setFilteredExpenses(() => {
const newValue = filterExpenses(expenses, value);
return newValue;
})
);
};
return (
<>
<NewExpense onAddNewExpense={addNewExpenseHandler} />
<ExpenseFilter expenses={expenses} />
<ShowExpenses
onChangeYear={changeYearHandler}
data={filteredExpenses}
/>
</>
);
}
export default App;
The problem is that filteredExpenses isn't up-to-date. It's always retarded and it's the previous state. I was trying to call a changeYearHandler in callback of setExpenses and setFilteredExpense inside setFilteredYear but it's still doesn't work and I don't know why.
It feels like you're using a roundabout way to filter your expenses. What about just creating a memoized version of a filteredExpenses directly, using useMemo()?
const filteredExpenses = useMemo(() => {
return expenses.filter((expense) => {
if (String(expense.date.getFullYear()) === filteredYear) {
return expense;
}
});
}, [expenses, filteredYear]);
The dependency array will ensure that whenever expenses or filteredYear changes, then filteredExpenses will recompute and return a new filtered array (that is subsequently cached).