perf(frontend): 递归组件 Memo 优化 — React.memo + useMemo

TaskTreeViewer:
- Modal/ReceiveConfirmModal/RejectModal/TransferModal 加 React.memo
- TaskNodeCard 递归组件加 React.memo,阻断父节点状态变更引发的整树重渲染
- countAllTasks 用 useMemo 缓存,仅在 task_tree 引用变化时重新计算

TaskListCard:
- TaskNode 递归组件加 React.memo
- countAll 用 useMemo 缓存
This commit is contained in:
2026-08-06 17:33:54 +08:00
parent 9d204a57d8
commit 3d164159ac
2 changed files with 25 additions and 15 deletions

View File

@ -1,4 +1,4 @@
import { useState, useCallback } from "react";
import { useState, useCallback, useMemo, memo } from "react";
import {
Search,
Loader2,
@ -75,7 +75,7 @@ function getStatusConfig(status: string) {
// 通用 Modal 容器
// ============================================================
function Modal({
const Modal = memo(function Modal({
open,
onClose,
title,
@ -110,13 +110,13 @@ function Modal({
</div>
</div>
);
}
});
// ============================================================
// 确认接收弹窗
// ============================================================
function ReceiveConfirmModal({
const ReceiveConfirmModal = memo(function ReceiveConfirmModal({
open,
task,
submitting,
@ -163,13 +163,13 @@ function ReceiveConfirmModal({
</div>
</Modal>
);
}
});
// ============================================================
// 品质驳回弹窗
// ============================================================
function RejectModal({
const RejectModal = memo(function RejectModal({
open,
task,
submitting,
@ -244,13 +244,13 @@ function RejectModal({
</form>
</Modal>
);
}
});
// ============================================================
// 完工裂变转交弹窗
// ============================================================
function TransferModal({
const TransferModal = memo(function TransferModal({
open,
task,
submitting,
@ -496,7 +496,7 @@ function TransferModal({
</form>
</Modal>
);
}
});
// ============================================================
// 单个任务节点卡片
@ -507,7 +507,7 @@ interface ModalTarget {
action: "receive" | "reject" | "transfer";
}
function TaskNodeCard({
const TaskNodeCard = memo(function TaskNodeCard({
task,
isLast,
onAction,
@ -653,7 +653,7 @@ function TaskNodeCard({
)}
</div>
);
}
});
// ============================================================
// 主容器组件
@ -779,6 +779,12 @@ export default function TaskTreeViewer() {
// ---- 渲染 ----
// 🚀 缓存递归计算 — 仅在 task_tree 变化时重新计算
const totalTaskCount = useMemo(
() => (product?.task_tree ? countAllTasks(product.task_tree) : 0),
[product?.task_tree],
);
const modalTask = modalTarget?.task ?? null;
return (
@ -880,7 +886,7 @@ export default function TaskTreeViewer() {
<p className="text-sm font-semibold text-gray-800">
{product.top_level_tasks?.length ?? 0}
{product.task_tree?.length
? ` · ${countAllTasks(product.task_tree)} 总计`
? ` · ${totalTaskCount} 总计`
: ""}
</p>
</div>