Stop infinite renders caused by circular $ref object properties#5118
Stop infinite renders caused by circular $ref object properties#5118heath-freenome wants to merge 2 commits into
Conversation
a681ff1 to
2957503
Compare
Fixes #3907, #4262 and #3826 - Adds RJSF_REF_CYCLE_KEY ('__rjsf_ref_cycle') to detect and terminate self-referential schemas that would otherwise render infinitely. - In resolveAllReferences, when a $ref is encountered that's already in the recurseList AND we're resolving in simple (non-resolveAnyOfOrOneOfRefs) mode, the schema is tagged with __rjsf_ref_cycle: true instead of silently returning the unresolved $ref. This only fires in a direct object-property context (!resolveAnyOfOrOneOfRefs), where rendering is unconditional and cycles are genuinely infinite. Array-items and anyOf/oneOf contexts remain untagged since they are data-driven and terminate naturally. - SchemaField checks _schema[RJSF_REF_CYCLE_KEY] after its useCallback hook (to satisfy React rules of hooks) and renders a CyclicSchemaField instead of recursing. CyclicSchemaField renders the CyclicSchemaExpandTemplate, which shows the user a message and an expand button to load the next cycle break. - hashForSchema now filters out keys starting with RJSF_REF_KEY before hashing so that internal cycle-tracking metadata does not affect the computed hash. - CyclicSchemaExpandTemplate is implemented for all theme packages: antd, chakra-ui, daisyui, fluentui-rc, mantine, mui, primereact, react-bootstrap, semantic-ui, and shadcn, each using their native UI library components. - Fixed buttonId in all CyclicSchemaExpandTemplate implementations to use fieldPathId[ID_KEY] (the $id string) instead of fieldPathId (the object). Button id format is now `${fieldPathId.$id}-button` (e.g. `root_child_child-button`), dropping the redundant `-${name}-` segment. - Added SchemaField test verifying that clicking the expand button renders the next level of the cycle, finding the button by its correct DOM id. - Updated FormSnap snapshot to reflect the corrected button id. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
In SJSF, I tried to solve this problem by generating a |
Hi @x0k... Yeah it's not until we encounter the second instance of the same key that the cycle is detected, hence the information showing up 2x for each cycle since the marker is on that second item. I've been debating doing something with the recursion such that upon the cycle detection, I can somehow go back to the first instance and mark that one instead. I may spend some more time on that once this gets merged |
FYI, if you use |
Reasons for making this change
Fixes #3907 and #4262
Checklist
npx nx run-many --target=build --exclude=@rjsf/docs && npm run test:updateto update snapshots, if needed.Preview
Screen.Recording.2026-06-07.at.8.00.42.PM.mov