本文详解如何将基于 `react-calendar-timeline` 的类组件(class component)完整迁移为现代 react 函数组件,涵盖状态管理(`usestate`)、事件处理、动态分组渲染及折叠/展开逻辑的函数式实现。
在 React 生态中,reac

以下是一个完整的、可直接运行的函数组件实现,它实现了原类组件的核心能力:树形分组结构、点击折叠/展开、动态标题渲染、父子关系映射及时间轴交互控制。
✅ 核心迁移要点
- 使用 useState 替代 this.state:groups、items 和 openGroups 均通过 useState 管理;
- 用箭头函数 + 闭包替代 bind(this):如 toggleGroup 直接接收 id 并更新 openGroups;
- 分组逻辑保持不变,但改用 map + 条件判断生成带 root/parent 属性的新分组数组;
- 渲染时通过 filter + map 动态生成可见分组,并为根节点注入 交互元素,子节点自动缩进;
? 示例代码(精简可运行版)
import React, { useState } from "react"; import moment from "moment"; import Timeline from "react-calendar-timeline"; import generateFakeData from "./generate-fake-data"; const keys = { groupIdKey: "id", groupTitleKey: "title", groupRightTitleKey: "rightTitle", itemIdKey: "id", itemTitleKey: "title", itemDivTitleKey: "title", itemGroupKey: "group", itemTimeStartKey: "start", itemTimeEndKey: "end", groupLabelKey: "title" }; const App = () => { const { groups: initialGroups, items: initialItems } = generateFakeData(); const defaultTimeStart = moment().startOf("day").toDate(); const defaultTimeEnd = moment().startOf("day").add(1, "day").toDate(); // 构建树形分组:每3个一组,第1个为 root,后2个为子节点 const newGroups = initialGroups.map((group) => { const idNum = parseInt(group.id); const isRoot = (idNum - 1) % 3 === 0; const parent = isRoot ? null : Math.floor((idNum - 1) / 3) * 3 + 1; return { ...group, root: isRoot, parent }; }); const [groups, setGroups] = useState(newGroups); const [items] = useState(initialItems); const [openGroups, setOpenGroups] = useState({}); const toggleGroup = (id) => { setOpenGroups((prev) => ({ ...prev, [id]: !prev[id] })); }; // 过滤并增强分组:仅显示 root 或其父节点已展开的子组 const filteredGroups = groups.filter((g) => g.root || openGroups[g.parent]); const groupsToShow = filteredGroups.map((group) => ({ ...group, title: group.root ? ( toggleGroup(parseInt(group.id))} style={{ cursor: "pointer" }} > {openGroups[parseInt(group.id)] ? "[-]" : "[+]"} {group.title} ) : ( {group.title} ) })); return (); }; export default App; ⚠️ 注意事项
- generateFakeData 需保持原样(返回 { groups, items } 结构),确保 id 字段为字符串数字(如 "1"),以便 parseInt() 安全转换;
- openGroups 是一个对象映射(如 { 1: true, 4: false }),避免使用数组索引,增强可读性与扩展性;
- 若需持久化展开状态(如刷新不丢失),可结合 useEffect + localStorage 实现;
- Timeline 组件本身不感知类/函数组件差异,所有 props 行为一致,迁移重点在于状态与事件逻辑的函数式重写。
该实现已在 CodeSandbox 验证通过,支持全部交互功能,是初学者从类组件迈向 Hooks 实践的理想范例。








