From 1508666e52f6792983001ff1bcda6df56462299c Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Thu, 11 Jun 2026 20:58:23 +0200 Subject: [PATCH] fix: DNS server edit modal showing defaults instead of saved values (#5155) The DNS server table columns were memoized with only [t] as deps, so they permanently captured the first render's openEditServer callback, which closed over the initial (null) dns settings. Clicking Edit then resolved the server to null and the modal fell back to default values. Stabilize openEditServer/deleteServer (and the fakedns equivalents) with useCallback and include them in the column memo deps so the columns refresh whenever the servers list changes. --- frontend/src/pages/xray/dns/DnsTab.tsx | 68 ++++++++++++------- frontend/src/pages/xray/dns/useDnsColumns.tsx | 6 +- 2 files changed, 44 insertions(+), 30 deletions(-) diff --git a/frontend/src/pages/xray/dns/DnsTab.tsx b/frontend/src/pages/xray/dns/DnsTab.tsx index 4b3a0b6d4..9fade2acc 100644 --- a/frontend/src/pages/xray/dns/DnsTab.tsx +++ b/frontend/src/pages/xray/dns/DnsTab.tsx @@ -128,6 +128,26 @@ export default function DnsTab({ templateSettings, setTemplateSettings }: DnsTab return list.map((server, idx) => ({ key: idx, server })); }, [dns?.servers]); + // Stable callbacks: the column definitions in useDnsServerColumns are + // memoized, so they must be able to depend on these (see issue #5155) + const openEditServer = useCallback( + (idx: number) => { + setEditingServer((dns?.servers || [])[idx] || null); + setEditingIndex(idx); + setServerModalOpen(true); + }, + [dns?.servers], + ); + const deleteServer = useCallback( + (idx: number) => { + mutate((tt) => { + const cfg = tt.dns as DnsConfig | undefined; + if (cfg?.servers) cfg.servers.splice(idx, 1); + }); + }, + [mutate], + ); + const dnsColumns = useDnsServerColumns({ openEditServer, deleteServer }); function openAddServer() { @@ -135,11 +155,6 @@ export default function DnsTab({ templateSettings, setTemplateSettings }: DnsTab setEditingIndex(null); setServerModalOpen(true); } - function openEditServer(idx: number) { - setEditingServer((dns?.servers || [])[idx] || null); - setEditingIndex(idx); - setServerModalOpen(true); - } function onServerConfirm(value: DnsServerValue) { mutate((tt) => { if (!tt.dns) return; @@ -150,12 +165,6 @@ export default function DnsTab({ templateSettings, setTemplateSettings }: DnsTab }); setServerModalOpen(false); } - function deleteServer(idx: number) { - mutate((tt) => { - const cfg = tt.dns as DnsConfig | undefined; - if (cfg?.servers) cfg.servers.splice(idx, 1); - }); - } function clearAllServers() { modal.confirm({ title: t('pages.xray.dns.clearAllTitle'), @@ -182,6 +191,28 @@ export default function DnsTab({ templateSettings, setTemplateSettings }: DnsTab return list.map((entry, idx) => ({ key: idx, ...entry })); }, [templateSettings?.fakedns]); + const deleteFakedns = useCallback( + (idx: number) => { + mutate((tt) => { + const list = tt.fakedns as FakednsRow[] | undefined; + if (!list) return; + list.splice(idx, 1); + if (list.length === 0) tt.fakedns = null; + }); + }, + [mutate], + ); + const updateFakednsField = useCallback( + (idx: number, field: 'ipPool' | 'poolSize', value: string | number) => { + mutate((tt) => { + const list = tt.fakedns as FakednsRow[] | undefined; + if (!list?.[idx]) return; + (list[idx] as unknown as Record)[field] = value; + }); + }, + [mutate], + ); + const fakednsColumns = useFakednsColumns({ deleteFakedns, updateFakednsField }); function addFakedns() { @@ -190,21 +221,6 @@ export default function DnsTab({ templateSettings, setTemplateSettings }: DnsTab (tt.fakedns as FakednsRow[]).push(DEFAULT_FAKEDNS()); }); } - function deleteFakedns(idx: number) { - mutate((tt) => { - const list = tt.fakedns as FakednsRow[] | undefined; - if (!list) return; - list.splice(idx, 1); - if (list.length === 0) tt.fakedns = null; - }); - } - function updateFakednsField(idx: number, field: 'ipPool' | 'poolSize', value: string | number) { - mutate((tt) => { - const list = tt.fakedns as FakednsRow[] | undefined; - if (!list?.[idx]) return; - (list[idx] as unknown as Record)[field] = value; - }); - } const items = useMemo(() => { const out = [ diff --git a/frontend/src/pages/xray/dns/useDnsColumns.tsx b/frontend/src/pages/xray/dns/useDnsColumns.tsx index 818b5a3c9..bb4bcbfcc 100644 --- a/frontend/src/pages/xray/dns/useDnsColumns.tsx +++ b/frontend/src/pages/xray/dns/useDnsColumns.tsx @@ -61,8 +61,7 @@ export function useDnsServerColumns({ render: (_v, record) => {expectedIPsFor(record.server)}, }, ], - // eslint-disable-next-line react-hooks/exhaustive-deps - [t], + [t, openEditServer, deleteServer], ); } @@ -116,7 +115,6 @@ export function useFakednsColumns({ ), }, ], - // eslint-disable-next-line react-hooks/exhaustive-deps - [], + [deleteFakedns, updateFakednsField], ); }