You've already forked DataMate
fix(components): 修复组件中定时器内存泄漏问题
- 在TopLoadingBar组件中添加timeoutRef并正确清理定时器 - 在Agent页面中添加timeoutRef管理AI响应模拟定时器 - 修复BasicInformation组件中useCallback依赖数组缺失问题 - 在CreateDataset页面中传递hidden属性控制数据源显示 - 在Orchestration页面中添加intervalRef管理工作流执行进度 - 在SynthesisTask中添加testTimeoutRef管理模板测试定时器 - 确保所有组件卸载时正确清除定时器避免内存泄漏
This commit is contained in:
@@ -151,6 +151,15 @@ export default function AgentPage() {
|
||||
const [isTyping, setIsTyping] = useState(false);
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
const inputRef = useRef<any>(null);
|
||||
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const scrollToBottom = () => {
|
||||
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
||||
@@ -174,8 +183,13 @@ export default function AgentPage() {
|
||||
setInputValue("");
|
||||
setIsTyping(true);
|
||||
|
||||
// 清理旧的 timeout
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current);
|
||||
}
|
||||
|
||||
// 模拟AI响应
|
||||
setTimeout(() => {
|
||||
timeoutRef.current = setTimeout(() => {
|
||||
const response = generateResponse(content);
|
||||
const assistantMessage: Message = {
|
||||
id: (Date.now() + 1).toString(),
|
||||
|
||||
@@ -78,7 +78,11 @@ export default function DatasetCreate() {
|
||||
onValuesChange={handleValuesChange}
|
||||
layout="vertical"
|
||||
>
|
||||
<BasicInformation data={newDataset} setData={setNewDataset} />
|
||||
<BasicInformation
|
||||
data={newDataset}
|
||||
setData={setNewDataset}
|
||||
hidden={["dataSource"]}
|
||||
/>
|
||||
</Form>
|
||||
</div>
|
||||
<div className="flex gap-2 justify-end p-6 border-top">
|
||||
|
||||
@@ -39,6 +39,7 @@ export default function BasicInformation({
|
||||
|
||||
// 获取归集任务
|
||||
const fetchCollectionTasks = useCallback(async () => {
|
||||
if (hidden.includes("dataSource")) return;
|
||||
try {
|
||||
const res = await queryTasksUsingGet({ page: 0, size: 100 });
|
||||
const tasks = Array.isArray(res?.data?.content)
|
||||
@@ -52,7 +53,7 @@ export default function BasicInformation({
|
||||
} catch (error) {
|
||||
console.error("Error fetching collection tasks:", error);
|
||||
}
|
||||
}, []);
|
||||
}, [hidden]);
|
||||
|
||||
const fetchParentDatasets = useCallback(async () => {
|
||||
if (hidden.includes("parentDatasetId")) return;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from "react";
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
import { Button, Card, Progress, Badge, Tabs } from "antd";
|
||||
import {
|
||||
GitBranch,
|
||||
@@ -272,6 +272,19 @@ export default function OrchestrationPage() {
|
||||
);
|
||||
const [showWorkflowEditor, setShowWorkflowEditor] = useState(false);
|
||||
|
||||
// 使用 useRef 保存 interval 引用
|
||||
const intervalRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
// 组件卸载时清理 interval
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (intervalRef.current) {
|
||||
clearInterval(intervalRef.current);
|
||||
intervalRef.current = null;
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const startNewFlow = () => {
|
||||
setShowWorkflowEditor(true);
|
||||
};
|
||||
@@ -310,8 +323,14 @@ export default function OrchestrationPage() {
|
||||
|
||||
setExecutions([newExecution, ...executions]);
|
||||
|
||||
// 清理旧的 interval
|
||||
if (intervalRef.current) {
|
||||
clearInterval(intervalRef.current);
|
||||
intervalRef.current = null;
|
||||
}
|
||||
|
||||
// 模拟执行进度
|
||||
const interval = setInterval(() => {
|
||||
intervalRef.current = setInterval(() => {
|
||||
setExecutions((prev) =>
|
||||
prev.map((exec) => {
|
||||
if (exec.id === newExecution.id) {
|
||||
@@ -319,6 +338,11 @@ export default function OrchestrationPage() {
|
||||
exec.progress + Math.random() * 10,
|
||||
100
|
||||
);
|
||||
// 进度完成时清理 interval
|
||||
if (newProgress >= 100 && intervalRef.current) {
|
||||
clearInterval(intervalRef.current);
|
||||
intervalRef.current = null;
|
||||
}
|
||||
return {
|
||||
...exec,
|
||||
progress: newProgress,
|
||||
@@ -334,8 +358,6 @@ export default function OrchestrationPage() {
|
||||
})
|
||||
);
|
||||
}, 500);
|
||||
|
||||
setTimeout(() => clearInterval(interval), 10000);
|
||||
};
|
||||
|
||||
const getStatusIcon = (status: string) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useRef } from "react";
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import {
|
||||
Card,
|
||||
Select,
|
||||
@@ -24,6 +24,7 @@ export default function InstructionTemplateCreate() {
|
||||
const [templates, setTemplates] = useState<Template[]>(mockTemplates);
|
||||
const [variables, setVariables] = useState<string[]>([]);
|
||||
const variableInputRef = useRef<Input | null>(null);
|
||||
const testTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
const [form] = Form.useForm();
|
||||
|
||||
@@ -70,12 +71,27 @@ export default function InstructionTemplateCreate() {
|
||||
setVariables(variables.filter((_, i) => i !== index));
|
||||
};
|
||||
|
||||
// 组件卸载时清理 timeout
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (testTimeoutRef.current) {
|
||||
clearTimeout(testTimeoutRef.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
// 测试模板
|
||||
const handleTestTemplate = async () => {
|
||||
const values = form.getFieldsValue();
|
||||
if (!values.prompt || !values.testInput) return;
|
||||
|
||||
// 清理旧的 timeout
|
||||
if (testTimeoutRef.current) {
|
||||
clearTimeout(testTimeoutRef.current);
|
||||
}
|
||||
|
||||
setIsTestingTemplate(true);
|
||||
setTimeout(() => {
|
||||
testTimeoutRef.current = setTimeout(() => {
|
||||
form.setFieldValue(
|
||||
"testOutput",
|
||||
`基于输入"${values.testInput}"生成的测试结果:
|
||||
|
||||
Reference in New Issue
Block a user