TypeError:无法读取 React Chart 组件中未定义的属性(读取“年份”),以及如何实现按年份过滤?

问题描述 投票:0回答:1

我在处理 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);
    }
});
reactjs node.js mongodb mern
1个回答
0
投票

1.解析API响应数据

根据您的 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);

您需要:

  1. 排除
    data
    数组的第一个元素。
const months = data.slice(1).map((item) => item._id.month);
  1. Node.JS API 返回的响应不包含第一个具有
    totalUsers
    totalNewUsers
    属性的对象。

2.反应日历
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
/>

3.当 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]);

演示@StackBlitz

© www.soinside.com 2019 - 2024. All rights reserved.