From dc8243cb51ec692a89e6050f2205e69162d64381 Mon Sep 17 00:00:00 2001 From: Owen Date: Sat, 6 Jun 2026 12:27:14 -0700 Subject: [PATCH] Fix form rendering issue --- src/components/BrowserGatewayTargetForm.tsx | 53 +++++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/src/components/BrowserGatewayTargetForm.tsx b/src/components/BrowserGatewayTargetForm.tsx index 768ac2738..09e6fd6fa 100644 --- a/src/components/BrowserGatewayTargetForm.tsx +++ b/src/components/BrowserGatewayTargetForm.tsx @@ -48,17 +48,46 @@ export type BrowserGatewayTargetFormProps = export function BrowserGatewayTargetForm( props: BrowserGatewayTargetFormProps ) { + // IDK MAN REMOVING THIS SEEMS TO CAUSE ISSUES + // Opt out of the React Compiler for this component. + // + // The parent (create page) shares a single `bgTargetForm` instance across + // multiple conditionally-rendered Form sections (SSH passthrough/push, RDP, + // VNC) and calls `bgTargetForm.reset(...)` in a useEffect when the + // resource type changes. react-hook-form's Controller uses an external + // subscription that the React Compiler cannot statically reason about, so + // with `reactCompiler: true` (see next.config.ts) the Compiler can memoize + // the render prop and skip re-rendering the elements when their + // bound form values change. The visible symptom is that typing into the + // destination/port inputs updates form state but the input itself never + // visually updates. The escape hatch is the canonical fix here. + "use no memo"; const t = useTranslations(); const [siteOpen, setSiteOpen] = useState(false); const sitesFieldName = props.multiSite === true ? props.sitesField : props.siteField; + // Subscribe to field values via useWatch and drive the controlled + // elements from these values rather than from the `field.value` returned + // by the Controller render prop. Combined with the "use no memo" directive + // above, this makes the inputs reliably re-render when their bound form + // values change. const watchedSites = useWatch({ control: props.control, name: sitesFieldName }); + const watchedDestination = useWatch({ + control: props.control, + name: props.destinationField + }); + + const watchedDestinationPort = useWatch({ + control: props.control, + name: props.destinationPortField + }); + const showMultiSiteDisclaimer = props.multiSite === true && ((watchedSites as Selectedsite[] | undefined)?.length ?? 0) > 1; @@ -141,7 +170,17 @@ export function BrowserGatewayTargetForm( {t("destination")} - + @@ -158,8 +197,16 @@ export function BrowserGatewayTargetForm( type="number" min={1} max={65535} - {...field} - value={field.value ?? ""} + name={field.name} + ref={field.ref} + onBlur={field.onBlur} + onChange={field.onChange} + value={ + (watchedDestinationPort as + | string + | number + | undefined) ?? "" + } />