Skip to content

Commit a66f788

Browse files
os-zhuangCopilot
andcommitted
fix(react): preserve data-obj-schema-invalid across re-renders
SchemaRenderer runs a post-mount forceUpdate to pick up lazy plugin registrations. The dev-mode validator deduped via a WeakSet that always returned { valid: true } on the second call, which stripped the data-obj-schema-invalid attribute on the immediate re-render and broke the 'marks invalid host elements with data-obj-schema-invalid' test. Split the two concerns: a WeakMap caches the validation outcome so the visual flag is stable across re-renders, while a WeakSet continues to dedupe console.warn so we still warn at most once per schema object. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent de87986 commit a66f788

2 files changed

Lines changed: 43 additions & 17 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
'@object-ui/react': patch
3+
---
4+
5+
fix(react): preserve `data-obj-schema-invalid` flag across re-renders
6+
7+
`SchemaRenderer` runs a post-mount `forceUpdate` to pick up lazy plugin
8+
registrations. The dev-mode validator was deduping via a `WeakSet` that
9+
always returned `valid: true` on the second call, which stripped the
10+
`data-obj-schema-invalid` attribute on the immediate re-render. The
11+
result and the "warn-once" tracking are now stored separately: a
12+
`WeakMap` caches the validation outcome (so the visual flag is stable),
13+
while a `WeakSet` continues to dedupe `console.warn` output.

packages/react/src/SchemaRenderer.tsx

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -44,37 +44,50 @@ const __DEV__ = (() => {
4444
}
4545
})();
4646

47-
const _validatedSchemas: WeakSet<object> =
47+
type _ValidationCacheEntry = { valid: boolean; messages: string[] };
48+
const _validationCache: WeakMap<object, _ValidationCacheEntry> =
49+
typeof WeakMap !== 'undefined'
50+
? new WeakMap()
51+
: ({ set() {}, get() { return undefined; }, has() { return false; } } as any);
52+
const _warnedSchemas: WeakSet<object> =
4853
typeof WeakSet !== 'undefined' ? new WeakSet() : ({ add() {}, has() { return false; } } as any);
4954

50-
function validateSchemaOnce(schema: any): { valid: boolean; messages: string[] } {
55+
function validateSchemaOnce(schema: any): _ValidationCacheEntry {
5156
if (!__DEV__ || !schema || typeof schema !== 'object') {
5257
return { valid: true, messages: [] };
5358
}
54-
if (_validatedSchemas.has(schema)) {
55-
// We don't cache the result intentionally — once a schema object has
56-
// been logged, we don't re-log on every re-render. Visual invalid flag
57-
// still derives from the structural presence of required fields below.
58-
return { valid: true, messages: [] };
59+
// Return cached result so re-renders (and the post-mount forceUpdate that
60+
// runs to pick up lazy plugin registrations) preserve the invalid flag.
61+
// Dedup of the console.warn is handled separately via _warnedSchemas.
62+
const cached = _validationCache.get(schema);
63+
if (cached) {
64+
return cached;
5965
}
60-
_validatedSchemas.add(schema);
66+
let entry: _ValidationCacheEntry = { valid: true, messages: [] };
6167
try {
6268
const result = validateSchema(schema);
6369
if (!result.valid) {
6470
const msgs = result.errors.map(e => `${e.path}: ${e.message}`);
65-
// eslint-disable-next-line no-console
66-
console.warn(
67-
'[ObjectUI] Invalid schema detected:\n' + msgs.join('\n'),
68-
schema
69-
);
70-
return { valid: false, messages: msgs };
71+
entry = { valid: false, messages: msgs };
72+
if (!_warnedSchemas.has(schema)) {
73+
_warnedSchemas.add(schema);
74+
// eslint-disable-next-line no-console
75+
console.warn(
76+
'[ObjectUI] Invalid schema detected:\n' + msgs.join('\n'),
77+
schema
78+
);
79+
}
7180
}
7281
} catch (err) {
7382
// Validator itself failed — surface but don't crash render.
74-
// eslint-disable-next-line no-console
75-
console.warn('[ObjectUI] Schema validator threw:', err);
83+
if (!_warnedSchemas.has(schema)) {
84+
_warnedSchemas.add(schema);
85+
// eslint-disable-next-line no-console
86+
console.warn('[ObjectUI] Schema validator threw:', err);
87+
}
7688
}
77-
return { valid: true, messages: [] };
89+
_validationCache.set(schema, entry);
90+
return entry;
7891
}
7992

8093
/**

0 commit comments

Comments
 (0)