代码示例
javascript
// 导入 React 和必要的 Hooks
import React, { FC, useMemo } from 'react'
// 从 recharts 库导入图表组件
import {
PieChart, // 饼图容器
Pie, // 饼图主体
Cell, // 饼图的每一个扇形"单元格"
Tooltip, // 提示框(鼠标悬停时显示详细数值)
ResponsiveContainer // 响应式容器,使图表能自适应父容器大小
} from 'recharts'
// 导入预定义的颜色常量数组,用于给不同的饼图扇形分配颜色
import { STAT_COLORS } from '../../../constant'
// 导入该组件所需的 Props 类型定义
import { QuestionRadioStatPropsType } from './interface'
/**
* 格式化数字工具函数
* 将小数转换为保留两位的百分比数值(字符串)
*
* @example 0.1234 -> "12.34"
*/
function format(n: number) {
return (n * 100).toFixed(2)
}
/**
* 单选题统计图表组件
*
* 该组件接收统计数据,计算总和,并渲染一个带有百分比标签和颜色区分的饼图。
*/
const StatComponent: FC<QuestionRadioStatPropsType> = ({ stat = [] }) => {
// --- 计算总和 (使用 useMemo 进行性能优化) ---
// 依赖项 [stat]:只有当 stat 数据变化时,才重新计算总和
// 如果不使用 useMemo,组件每次重新渲染都会执行一次 forEach 循环,浪费性能
const sum = useMemo(() => {
let s = 0
stat.forEach(i => (s += i.count))
return s
}, [stat])
return (
// 外层容器设置了固定的宽高 (300x400),作为图表的宿主
<div style=>
{/*
响应式容器,包裹图表。
width="100%" height="100%" 表示图表将填满父容器。
*/}
<ResponsiveContainer width="100%" height="100%">
{/* 饼图根组件 */}
<PieChart>
{/*
Pie 组件:定义具体的饼图数据和样式
dataKey="count": 指定数据对象中哪个字段决定扇形的大小(占比)
data={stat}: 绑定传入的统计数据数组
cx="50%" / cy="50%": 设置饼图在容器中的中心位置(水平垂直居中)
outerRadius={50}: 设置饼图的外半径大小(单位:px)
fill="#8884d8": 设置默认填充色(如果 Cell 没有指定颜色,会使用这个)
label: 定义扇形上的标签内容
接收一个函数,参数 i 代表当前扇形对应的数据项
返回字符串:例如 "选项A: 50.00%"
*/}
<Pie
dataKey="count"
data={stat}
cx="50%"
cy="50%"
outerRadius={50}
fill="#8884d8"
label={i => `${i.name}: ${format(i.count / sum)}%`}
>
{/*
动态渲染扇形颜色
遍历 stat 数据,为每一个数据项生成一个 <Cell>
Cell 用于定义每一个扇形的具体样式(主要是 fill 颜色)
使用 STAT_COLORS[index] 从预定义的颜色数组中按索引取色
key={index}:虽然不推荐仅用 index 作为 key,但在这里图表渲染是合理的
*/}
{stat.map((i, index) => {
return <Cell key={index} fill={STAT_COLORS[index]} />
})}
</Pie>
{/* 鼠标悬停提示框 */}
<Tooltip />
</PieChart>
</ResponsiveContainer>
</div>
)
}
export default StatComponent
核心逻辑解析
💡 核心逻辑解析
1. useMemo 的作用
- 场景: 计算所有选项的总票数(
sum)。 - 原因: 如果
stat数组很大,或者计算逻辑很复杂,每次组件渲染都重新计算会浪费性能。 - 效果:
useMemo确保只有当stat数据真正发生变化时,才重新执行forEach循环去累加求和。如果stat没变,直接复用上一次计算的结果。
2. label 回调函数
label属性允许你自定义扇形上的文字。i.count / sum计算出当前选项的占比(小数)。format(...)将小数转换为保留两位的百分比字符串。- 最终显示效果类似:
A: 25.00%。
3. <Cell> 的作用
<Pie>标签上的fill属性是设置"默认颜色"。<Cell>标签用于给每一个独立的扇形设置颜色。- 代码中通过
stat.map遍历数据,将预定义的颜色数组STAT_COLORS中的颜色依次分配给每个选项,从而实现多彩饼图的效果。