fix(ui): prevent infinite re-render loop on agent configure page

MarkdownEditor's plugins useMemo depended on imageUploadHandler, which
was a new arrow function on every parent render. This caused MDXEditor
to reinitialize plugins on each render, firing onChange, which updated
the overlay state in AgentConfigForm, triggering a parent re-render —
creating an infinite "Maximum update depth exceeded" loop.

Fix: use a stable ref for imageUploadHandler so plugins are only created
once (keyed on whether a handler exists, not its identity). Also use a
module-level empty object for env config fallback to avoid unnecessary
EnvVarEditor re-renders.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dotta
2026-03-03 09:36:49 -06:00
parent 535f22dcaf
commit 69ba77f334
2 changed files with 18 additions and 5 deletions

View File

@@ -208,6 +208,10 @@ export const MarkdownEditor = forwardRef<MarkdownEditorRef, MarkdownEditorProps>
const [isDragOver, setIsDragOver] = useState(false);
const dragDepthRef = useRef(0);
// Stable ref for imageUploadHandler so plugins don't recreate on every render
const imageUploadHandlerRef = useRef(imageUploadHandler);
imageUploadHandlerRef.current = imageUploadHandler;
// Mention state (ref kept in sync so callbacks always see the latest value)
const [mentionState, setMentionState] = useState<MentionState | null>(null);
const mentionStateRef = useRef<MentionState | null>(null);
@@ -235,11 +239,17 @@ export const MarkdownEditor = forwardRef<MarkdownEditorRef, MarkdownEditorProps>
},
}), []);
// Whether the image plugin should be included (boolean is stable across renders
// as long as the handler presence doesn't toggle)
const hasImageUpload = Boolean(imageUploadHandler);
const plugins = useMemo<RealmPlugin[]>(() => {
const imageHandler = imageUploadHandler
const imageHandler = hasImageUpload
? async (file: File) => {
const handler = imageUploadHandlerRef.current;
if (!handler) throw new Error("No image upload handler");
try {
const src = await imageUploadHandler(file);
const src = await handler(file);
setUploadError(null);
return src;
} catch (err) {
@@ -268,7 +278,7 @@ export const MarkdownEditor = forwardRef<MarkdownEditorRef, MarkdownEditorProps>
all.push(imagePlugin({ imageUploadHandler: imageHandler }));
}
return all;
}, [imageUploadHandler]);
}, [hasImageUpload]);
useEffect(() => {
if (value !== latestValueRef.current) {