React中useMemo的使用

代码示例

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 中的颜色依次分配给每个选项,从而实现多彩饼图的效果。
相关推荐
1024肥宅2 小时前
JS复杂去重一定要先排序吗?深度解析与性能对比
前端·javascript·面试
趣知岛2 小时前
JavaScript性能优化实战大纲
开发语言·javascript·性能优化
im_AMBER3 小时前
weather-app开发手记 04 AntDesign组件库使用解析 | 项目设计困惑
开发语言·前端·javascript·笔记·学习·react.js
用泥种荷花3 小时前
VueCropper加载OBS图片跨域问题
前端
董世昌413 小时前
什么是事件冒泡?如何阻止事件冒泡和浏览器默认事件?
java·前端
Bigger3 小时前
在 React 里优雅地 “隐藏 iframe 滚动条”
前端·css·react.js
小沐°3 小时前
vue3-ElementPlus出现Uncaught (in promise) cancel 报错
前端·javascript·vue.js
栀秋6663 小时前
LangChain Memory 实战指南:让大模型记住你每一句话,轻松打造“有记忆”的AI助手
javascript·langchain·llm
四瓣纸鹤3 小时前
F2图表在Vue3中的使用方法
前端·javascript·vue.js·antv/f2