Explorar el Código

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.
MHSanaei hace 17 horas
padre
commit
1508666e52

+ 42 - 26
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 }));
     return list.map((server, idx) => ({ key: idx, server }));
   }, [dns?.servers]);
   }, [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 });
   const dnsColumns = useDnsServerColumns({ openEditServer, deleteServer });
 
 
   function openAddServer() {
   function openAddServer() {
@@ -135,11 +155,6 @@ export default function DnsTab({ templateSettings, setTemplateSettings }: DnsTab
     setEditingIndex(null);
     setEditingIndex(null);
     setServerModalOpen(true);
     setServerModalOpen(true);
   }
   }
-  function openEditServer(idx: number) {
-    setEditingServer((dns?.servers || [])[idx] || null);
-    setEditingIndex(idx);
-    setServerModalOpen(true);
-  }
   function onServerConfirm(value: DnsServerValue) {
   function onServerConfirm(value: DnsServerValue) {
     mutate((tt) => {
     mutate((tt) => {
       if (!tt.dns) return;
       if (!tt.dns) return;
@@ -150,12 +165,6 @@ export default function DnsTab({ templateSettings, setTemplateSettings }: DnsTab
     });
     });
     setServerModalOpen(false);
     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() {
   function clearAllServers() {
     modal.confirm({
     modal.confirm({
       title: t('pages.xray.dns.clearAllTitle'),
       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 }));
     return list.map((entry, idx) => ({ key: idx, ...entry }));
   }, [templateSettings?.fakedns]);
   }, [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<string, unknown>)[field] = value;
+      });
+    },
+    [mutate],
+  );
+
   const fakednsColumns = useFakednsColumns({ deleteFakedns, updateFakednsField });
   const fakednsColumns = useFakednsColumns({ deleteFakedns, updateFakednsField });
 
 
   function addFakedns() {
   function addFakedns() {
@@ -190,21 +221,6 @@ export default function DnsTab({ templateSettings, setTemplateSettings }: DnsTab
       (tt.fakedns as FakednsRow[]).push(DEFAULT_FAKEDNS());
       (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<string, unknown>)[field] = value;
-    });
-  }
 
 
   const items = useMemo(() => {
   const items = useMemo(() => {
     const out = [
     const out = [

+ 2 - 4
frontend/src/pages/xray/dns/useDnsColumns.tsx

@@ -61,8 +61,7 @@ export function useDnsServerColumns({
         render: (_v, record) => <span className="muted">{expectedIPsFor(record.server)}</span>,
         render: (_v, record) => <span className="muted">{expectedIPsFor(record.server)}</span>,
       },
       },
     ],
     ],
-    // 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],
   );
   );
 }
 }