I've built a toolbar that allows a user to filter data via dates and a search bar. The data that's filtered (expenses) is being fed into a child component and although those props are being changed on button click, the child component itself is not being re-rendered and thus is displaying the original unfiltered data.
This is the parent component:
const Row1 = () => {
const isAboveMediumScreen = useMediaQuery("(min-width: 1200px)");
let { data: expenses, isLoading } = useGetExpensesQuery();
const { data: categories } = useGetCategoriesQuery();
const [start, setStart] = React.useState<Dayjs | null>(null);
const [end, setEnd] = React.useState<Dayjs | null>(null);
const [category, setCategory] = React.useState<string>("");
useEffect(() => {
setStart(getEarliest(expenses));
setEnd(getLatest(expenses));
setCategory("All categories");
}, [expenses]);
const handleChange = (event: SelectChangeEvent<string>) => {
setCategory(event.target.value);
};
const handleFilter = () => {
console.log(start, end, category);
expenses = expenses?.filter((expense) => {
let date = dayjs(expense.date);
if (!date.isBefore(start) && !date.isAfter(end)) {
return date;
}
});
console.log(expenses);
};
if (!expenses) {
return null;
}
return (
<LineChartComponent expenses={expenses} isLoading={isLoading} />
);
};
export default Row1;
And the child component (LineChartComponent) is as follows:
const LineChartComponent: React.FC<{
expenses: Expense[];
isLoading: boolean;
}> = ({ expenses, isLoading }) => {
const finalDate = useMemo(() => {
console.log("line chart expenses", expenses);
const finalDate: { date: string; expenses: number }[] = [];
if (expenses) {
const orderedDates = expenses
.map((expense) => ({
...expense,
date: new Date(expense.date!).getMonth(),
}))
.sort((a, b) => a.date - b.date);
const reduced = orderedDates.reduce(
(map, { date, price, subCategory, userId, category }) =>
map.set(date, (map.get(date) ?? 0) + price),
new Map()
);
reduced.forEach((price, date) => {
finalDate.push({
expenses: price,
date: monthNames[date].substring(0, 3),
});
});
return finalDate;
}
return [];
}, [expenses]);
const renderLegendText = (value: string) => {
return <span>{value}($)</span>;
};
return (
<ResponsiveContainer width="100%" height="100%">
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
width: "100%",
height: "100%",
}}
>
{isLoading ? (
<Typography variant="h4" sx={{ color: "#12efc8" }}>
Loading...
</Typography>
) : (
<LineChart
width={400}
height={230}
data={finalDate}
margin={{
// top: 5,
right: 30,
// left: 20,
// bottom: 5,
}}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="date" />
<YAxis type="number" domain={["dataMin-100", "dataMax+100"]} />
<Tooltip />
<Legend formatter={renderLegendText} />
<Line type="monotone" dataKey="expenses" stroke="#12efc8" />
</LineChart>
)}
</div>
</ResponsiveContainer>
);
};
export default LineChartComponent;
I'm using useMemo to in my LineChartComponent because I want to prevent the same logic on re-render and I'm not sure how to incorporate useEffect with useMemo. I'm thinking useEffect here to test for prop change but I'm not sure if that's correct