Skip to content

Commit 0032b2a

Browse files
authored
[Flight] Log error if prod elements are rendered (#34189)
1 parent 14c50e3 commit 0032b2a

File tree

3 files changed

+46
-1
lines changed

3 files changed

+46
-1
lines changed

packages/internal-test-utils/consoleMock.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ export function createLogAssertion(
355355
let argIndex = 0;
356356
// console.* could have been called with a non-string e.g. `console.error(new Error())`
357357
// eslint-disable-next-line react-internal/safe-string-coercion
358-
String(format).replace(/%s|%c/g, () => argIndex++);
358+
String(format).replace(/%s|%c|%o/g, () => argIndex++);
359359
if (argIndex !== args.length) {
360360
if (format.includes('%c%s')) {
361361
// We intentionally use mismatching formatting when printing badging because we don't know

packages/react-server/src/ReactFlightServer.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3354,6 +3354,27 @@ function renderModelDestructive(
33543354
task.debugOwner = element._owner;
33553355
task.debugStack = element._debugStack;
33563356
task.debugTask = element._debugTask;
3357+
if (
3358+
element._owner === undefined ||
3359+
element._debugStack === undefined ||
3360+
element._debugTask === undefined
3361+
) {
3362+
let key = '';
3363+
if (element.key !== null) {
3364+
key = ' key="' + element.key + '"';
3365+
}
3366+
3367+
console.error(
3368+
'Attempted to render <%s%s> without development properties. ' +
3369+
'This is not supported. It can happen if:' +
3370+
'\n- The element is created with a production version of React but rendered in development.' +
3371+
'\n- The element was cloned with a custom function instead of `React.cloneElement`.\n' +
3372+
'The props of this element may help locate this element: %o',
3373+
element.type,
3374+
key,
3375+
element.props,
3376+
);
3377+
}
33573378
// TODO: Pop this. Since we currently don't have a point where we can pop the stack
33583379
// this debug information will be used for errors inside sibling properties that
33593380
// are not elements. Leading to the wrong attribution on the server. We could fix

packages/react-server/src/__tests__/ReactFlightServer-test.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ let ReactNoopFlightServer;
3636
let Scheduler;
3737
let advanceTimersByTime;
3838
let assertLog;
39+
let assertConsoleErrorDev;
3940

4041
describe('ReactFlight', () => {
4142
beforeEach(() => {
@@ -64,6 +65,7 @@ describe('ReactFlight', () => {
6465
Scheduler = require('scheduler');
6566
const InternalTestUtils = require('internal-test-utils');
6667
assertLog = InternalTestUtils.assertLog;
68+
assertConsoleErrorDev = InternalTestUtils.assertConsoleErrorDev;
6769
});
6870

6971
afterEach(() => {
@@ -175,4 +177,26 @@ describe('ReactFlight', () => {
175177
stackTwo: '\n in OwnerStackDelayed (at **)' + '\n in App (at **)',
176178
});
177179
});
180+
181+
it('logs an error when prod elements are rendered', async () => {
182+
const element = ReactServer.createElement('span', {
183+
key: 'one',
184+
children: 'Free!',
185+
});
186+
ReactNoopFlightServer.render(
187+
// bad clone
188+
{...element},
189+
);
190+
191+
assertConsoleErrorDev([
192+
[
193+
'Attempted to render <span key="one"> without development properties. This is not supported. It can happen if:' +
194+
'\n- The element is created with a production version of React but rendered in development.' +
195+
'\n- The element was cloned with a custom function instead of `React.cloneElement`.\n' +
196+
"The props of this element may help locate this element: { children: 'Free!', [key]: [Getter] }",
197+
{withoutStack: true},
198+
],
199+
"TypeError: Cannot read properties of undefined (reading 'stack')",
200+
]);
201+
});
178202
});

0 commit comments

Comments
 (0)