我在处理 React 项目时遇到 TypeError,其中我使用图表组件来显示从 API 获取的用户分析数据。此外,我正在寻求有关如何按年份实施过滤的指导。这是我看到的错误消息:
TypeError: Cannot read properties of undefined (reading 'year')
代码片段:
图表组件:
import "./Chart.scss";
import { LineChart, Line, XAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
import React, { useState } from 'react'; // Don't forget to import React if you're using JSX
import Calendar from 'react-calendar'; // Import the Calendar component
const Chart = ({ title, data, datakeys, grid }) => {
const [selectedYear, setSelectedYear] = useState(new Date().getFullYear()); // Initialize selectedYear state with the current year
// Extracting month values from the data array
const months = data.map(item => item._id.month);
// Filter data based on the selected year
const filteredData = data.filter(item => item._id.year === selectedYear);
// Handle calendar change
const handleCalendarChange = date => {
setSelectedYear(date.getFullYear());
};
return (
<div className="chart">
<div className="topOfchart">
<div className="left">
<h3 className="chartTitle">
{title}
</h3>
</div>
<div className="right">
<Calendar
onChange={handleCalendarChange}
value={new Date(selectedYear, 0, 1)} // Set the initial value to January 1st of the selected year
view="year" // Display the calendar in year view
/>
</div>
</div>
<ResponsiveContainer width="100%" aspect={4/1}>
<LineChart data={filteredData}>
<XAxis dataKey="_id.month" stroke="#5550bd" tick={{fontSize: 12}} /> {/* Accessing the month value from the _id object */}
<Line type="monotone" dataKey={datakeys} stroke="#5550bd"/>
<Tooltip/>
{ grid && <CartesianGrid stroke="#e0dfdf" strokeDasharray="5 5"/> }
</LineChart>
</ResponsiveContainer>
</div>
);
};
export default Chart;
和主页组件:
import Chart from "../../components/chart/Chart"
import FeaturedInfo from "../../components/featuredinfo/FeaturedInfo"
import { WidgetLg } from "../../components/widgetLg/WidgetLg"
import { WidgetSm } from "../../components/widgetSm/WidgetSm"
import "./Home.scss"
import {Data} from "../../data";
import { useEffect, useState } from "react";
import api from "../../api";
const Home = () => {
const MONTHS=[
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Agu", "Sep", "Oct", "Nov", "Dec"
];
const [userStats, setUserStats] = useState([]);
useEffect(()=>{
const getStats = async ()=>{
try {
const res = await api.get("/users/stats",{
headers:{
token:
"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY1YjkxMWYwMjM5MjAxMTBhNTA3NGJmNCIsImlzQWRtaW4iOnRydWUsImlhdCI6MTcxMTcwMzAzMiwiZXhwIjoxNzE0NzI3MDMyfQ.ClT9x0c6v_Bfm9zvhmDAwUPDHWkm-Ws-yZ1CfNhX-5Y"
}
});
setUserStats(res.data)
} catch (error) {
console.log(error);
}
};
getStats();
},[])
console.log(userStats);
return (
<div className="home">
<FeaturedInfo/>
<Chart data={userStats} title="User Analytics" grid datakeys="newUsers"/>
<div className="homeWidgets">
<WidgetSm/>
<WidgetLg/>
</div>
</div>
)
}
export default Home
这是后端代码:
router.get("/stats", async (req, res) => {
const today = new Date();
const lastMonth = new Date(today.getFullYear(), today.getMonth() - 1, today.getDate()); // Get the date of last month
const monthsArray = [
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
];
try {
// Calculate total users of all time
const totalUsersData = await User.aggregate([
{
$group: {
_id: null,
totalUsers: { $sum: 1 }
}
}
]);
// Calculate total new users of last month
const totalNewUsersData = await User.aggregate([
{
$match: { createdAt: { $gte: lastMonth } } // Filter users created within the last month
},
{
$group: {
_id: null,
totalNewUsers: { $sum: 1 }
}
}
]);
let totalUsers;
if (totalUsersData.length > 0) {
totalUsers = totalUsersData[0].totalUsers;
} else {
totalUsers = 0;
}
let totalNewUsers;
if (totalNewUsersData.length > 0) {
totalNewUsers = totalNewUsersData[0].totalNewUsers;
} else {
totalNewUsers = 0;
}
// Retrieve monthly user data
const monthlyUserData = await User.aggregate([
{
$group: {
_id: {
year: { $year: "$createdAt" },
month: { $arrayElemAt: [monthsArray, { $subtract: [{ $month: "$createdAt" }, 1] }] }
},
total: { $sum: 1 }, // Total users
newUsers: {
$sum: {
$cond: [
{ $gte: ["$createdAt", lastMonth] }, // Check if user was created within the last month
1,
0
]
}
}
}
},
{
$project: {
_id: 1,
total: 1,
newUsers: 1
}
}
]);
const data = [
{
totalUsers: totalUsers,
totalNewUsers: totalNewUsers
},
...monthlyUserData
];
res.status(200).json(data);
} catch (error) {
res.status(500).json(error);
}
});
根据您的 Node.JS API 返回的数据,如下所示:
[
{
totalUsers: 1150,
totalNewUsers: 160,
},
{
_id: {
month: 12,
year: 2023,
},
total: 990,
newUsers: 15,
},
...
]
第一个元素不会具有
_id
属性。
我相信这两行会抛出错误,因为
_id
不存在。
const months = data.map(item => item._id.month);
// Filter data based on the selected year
const filteredData = data.filter(item => item._id.year === selectedYear);
您需要:
data
数组的第一个元素。const months = data.slice(1).map((item) => item._id.month);
totalUsers
和 totalNewUsers
属性的对象。onChange
事件同时,我注意到
onChange
事件不会触发年视图中的handleCalendarChange
。相反,您应该使用 onClickMonth
事件。
<Calendar
onClickMonth={handleCalendarChange}
value={new Date(selectedYear, 0, 1)} // Set the initial value to January 1st of the selected year
view="year" // Display the calendar in year view
/>
useEffect
改变时,使用 React hook
filteredData
来更新 selectedYear
组件初始化时,
filteredData
只会初始化,不会更新。更改 filteredData
来存储状态变量。应用 useEffect
并以 selectedYear
作为依赖项,因此当它发生更改时,您需要相应地更新 filteredData
。
const [filteredData, setFilteredData] = useState([])
useEffect(() => {
const _filteredData = data
.slice(1)
.filter((item) => item._id.year === selectedYear)
setFilteredData(_filteredData);
}, [selectedYear]);