From 3c636bcaa2a235f185141ec3c2b51bccdc9b20b3 Mon Sep 17 00:00:00 2001 From: Johnton Chen <504071088@qq.com> Date: Mon, 15 Jun 2026 23:05:56 +0800 Subject: [PATCH] refactor(copy-rule): extract context and improve batch clone modal - Extract MultiCopyPopupContext to standalone context.ts file - Add Task type and improve type safety in BatchCloneModal - Use useRef/useCallback to stabilize batch clone handlers - Add cleanup effect for selectRules in RuleCheckCard on unmount Co-Authored-By: Claude Opus 4.7 --- .../components/CopyRule/Multi‌CopyPopup.tsx | 21 +-------- .../CopyRule/comp/BatchCloneModal.tsx | 46 +++++++++++-------- .../CopyRule/comp/RuleCheckCard.tsx | 17 ++++--- .../rules/components/CopyRule/comp/Step1.tsx | 2 +- .../rules/components/CopyRule/comp/Step2.tsx | 2 +- .../rules/components/CopyRule/context.ts | 21 +++++++++ 6 files changed, 61 insertions(+), 48 deletions(-) create mode 100644 src/pages/rules/components/CopyRule/context.ts diff --git a/src/pages/rules/components/CopyRule/Multi‌CopyPopup.tsx b/src/pages/rules/components/CopyRule/Multi‌CopyPopup.tsx index c8a16cb..801d796 100644 --- a/src/pages/rules/components/CopyRule/Multi‌CopyPopup.tsx +++ b/src/pages/rules/components/CopyRule/Multi‌CopyPopup.tsx @@ -4,7 +4,7 @@ import { useBreakpoint, } from '@ant-design/pro-components'; import { Button, Divider, Flex, Input, Modal, Space, Typography } from 'antd'; -import { createContext, useMemo, useRef, useState } from 'react'; +import { useMemo, useRef, useState } from 'react'; import useRichFormat from '@/hooks/useRichI18n'; import { $t } from '@/utils/i18n'; import '../style.less'; @@ -14,26 +14,9 @@ import { handleEmptyValuesFunc } from '../../utils'; import BatchCloneModal from './comp/BatchCloneModal'; import Step1 from './comp/Step1'; import Step2 from './comp/Step2'; +import { MultiCopyPopupContext } from './context'; const { Text, Title } = Typography; -type MultiCopyPopupContextValue = { - ruleMeta: Record; - dataSourceList: API.DataSourceListItem[]; - currentRuleType: number; - targetDataSourceId: string | number | null; - selectRules: API.RuleListItem[]; - setSelectRules: (rules: API.RuleListItem[]) => void; - currentSchemaMap: Record; -}; -export const MultiCopyPopupContext = createContext({ - ruleMeta: {}, - dataSourceList: [], - currentRuleType: 0, - targetDataSourceId: null, - selectRules: [], - setSelectRules: () => {}, - currentSchemaMap: {}, -}); type MultiCopyPopupProps = { visible: boolean; ruleMeta: Record; diff --git a/src/pages/rules/components/CopyRule/comp/BatchCloneModal.tsx b/src/pages/rules/components/CopyRule/comp/BatchCloneModal.tsx index da86944..0553332 100644 --- a/src/pages/rules/components/CopyRule/comp/BatchCloneModal.tsx +++ b/src/pages/rules/components/CopyRule/comp/BatchCloneModal.tsx @@ -17,10 +17,13 @@ import { Tag, Tooltip, } from 'antd'; -import { useEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { processSingleRule } from '@/pages/rules/utils'; import { $t } from '@/utils/i18n'; +type TaskStatus = 'waiting' | 'processing' | 'success' | 'warning' | 'error'; +type Task = { name: string; status: TaskStatus; errorMsg: string }; + type BatchCloneModalProps = { visible: boolean; selectedRows: Partial[]; @@ -33,26 +36,33 @@ const BatchCloneModal = ({ onClose, onFinish, }: BatchCloneModalProps) => { - const [tasks, setTasks] = useState([]); + const [tasks, setTasks] = useState([]); const [isRunning, setIsRunning] = useState(false); const [currentRunning, setCurrentRunning] = useState(0); + const selectedRowsRef = useRef(selectedRows); + selectedRowsRef.current = selectedRows; useEffect(() => { - const initialTasks = selectedRows.map((row) => ({ - name: row.name, - status: 'waiting', // waiting, processing, success, error - errorMsg: '', - })); - setTasks(initialTasks); - }, [visible, selectedRows]); + if (visible) { + setTasks( + selectedRows.map((row) => ({ + name: row.name || '', + status: 'waiting', + errorMsg: '', + })), + ); + setIsRunning(false); + setCurrentRunning(0); + } + }, [visible]); const finishedTasks = useMemo( - () => tasks.filter((t) => ['success', 'warning'].includes(t.status)), + () => tasks.filter((t) => t.status === 'success' || t.status === 'warning'), [tasks], ); const finishedMessage = useMemo(() => { - const hasWarning = finishedTasks.find((t) => t.status === 'warning'); + const hasWarning = finishedTasks.some((t) => t.status === 'warning'); const type: AlertProps['type'] = hasWarning ? 'warning' : finishedTasks.length === tasks.length @@ -69,9 +79,10 @@ const BatchCloneModal = ({ }; }, [finishedTasks, tasks]); - const startBatch = async () => { + const startBatch = useCallback(async () => { + const rows = selectedRowsRef.current; setIsRunning(true); - for (let i = 0; i < selectedRows.length; i++) { + for (let i = 0; i < rows.length; i++) { setCurrentRunning(i + 1); setTasks((prev) => { const next = [...prev]; @@ -79,8 +90,7 @@ const BatchCloneModal = ({ return next; }); try { - await processSingleRule({ payload: selectedRows[i] }); - + await processSingleRule({ payload: rows[i] }); setTasks((prev) => { const next = [...prev]; next[i].status = 'success'; @@ -96,13 +106,13 @@ const BatchCloneModal = ({ } } setIsRunning(false); - }; + }, []); - const handleReset = () => { + const handleReset = useCallback(() => { setTasks([]); setIsRunning(false); setCurrentRunning(0); - }; + }, []); return ( void; }; const RuleCheckCard = ({ dataSourceId }: RuleCheckCardProps) => { - const [ruleList, setRuleList] = useState([]); - const { ruleMeta, currentRuleType, setSelectRules } = useContext( MultiCopyPopupContext, ); + const [ruleList, setRuleList] = useState([]); const { loading } = useRequest( () => @@ -25,7 +23,6 @@ const RuleCheckCard = ({ dataSourceId }: RuleCheckCardProps) => { dataSourceId, }), { - // manual: true, refreshDeps: [dataSourceId, currentRuleType], onSuccess: ({ data = [] }: { data: API.RuleListItem[] }) => { setRuleList(data); @@ -37,15 +34,17 @@ const RuleCheckCard = ({ dataSourceId }: RuleCheckCardProps) => { }, ); + useEffect(() => { + return () => setSelectRules([]); + }, []); + if (!dataSourceId) return
{$t('pages.clones.step1.tips')}
; if (loading) return ; - // const - return ( ; + dataSourceList: API.DataSourceListItem[]; + currentRuleType: number; + targetDataSourceId: string | number | null; + selectRules: API.RuleListItem[]; + setSelectRules: (rules: API.RuleListItem[]) => void; + currentSchemaMap: Record; +}; + +export const MultiCopyPopupContext = createContext({ + ruleMeta: {}, + dataSourceList: [], + currentRuleType: 0, + targetDataSourceId: null, + selectRules: [], + setSelectRules: () => {}, + currentSchemaMap: {}, +});