Skip to content

Commit 1b0c594

Browse files
committed
Warn if outdated JSX transform is detected
We want to warn if we detect that an app is using an outdated JSX transform. We can't just warn if `createElement` is called because we still support `createElement` when it's called manually. We only want to warn if `createElement` is output by the compiler. The heuristic is to check for a `__self` prop, which is an optional, internal prop that older transforms used to pass to `createElement` for better debugging in development mode. If `__self` is present, we `console.warn` once with advice to upgrade to the modern JSX transform. Subsequent elements will not warn. There's a special case we have to account for: when a static "key" prop is defined _after_ a spread, the modern JSX transform outputs `createElement` instead of `jsx`. (This is because with `jsx`, a spread key always takes precedence over a static key, regardless of the order, whereas `createElement` respects the order.) To avoid a false positive warning, we skip the warning whenever a `key` prop is present.
1 parent f613165 commit 1b0c594

File tree

3 files changed

+53
-2
lines changed

3 files changed

+53
-2
lines changed

packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ describe('ReactDeprecationWarnings', () => {
9191
);
9292
});
9393

94+
// Disabling this until #28732 lands so we can assert on the warning message.
95+
// (It's already disabled in all but the Meta builds, anyway. Nbd.)
96+
// @gate TODO || !__DEV__
9497
// @gate !disableStringRefs
9598
it('should warn when owner and self are the same for string refs', async () => {
9699
class RefComponent extends React.Component {
@@ -114,8 +117,11 @@ describe('ReactDeprecationWarnings', () => {
114117
await waitForAll([]);
115118
});
116119

120+
// Disabling this until #28732 lands so we can assert on the warning message.
121+
// (It's already disabled in all but the Meta builds, anyway. Nbd.)
122+
// @gate TODO || !__DEV__
117123
// @gate !disableStringRefs
118-
it('should warn when owner and self are different for string refs', async () => {
124+
it('should warn when owner and self are different for string refs (createElement)', async () => {
119125
class RefComponent extends React.Component {
120126
render() {
121127
return null;
@@ -143,7 +149,7 @@ describe('ReactDeprecationWarnings', () => {
143149

144150
// @gate __DEV__
145151
// @gate !disableStringRefs
146-
it('should warn when owner and self are different for string refs', async () => {
152+
it('should warn when owner and self are different for string refs (jsx)', async () => {
147153
class RefComponent extends React.Component {
148154
render() {
149155
return null;

packages/react/src/__tests__/ReactCreateElement-test.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,4 +466,27 @@ describe('ReactCreateElement', () => {
466466
});
467467
expect(test.props.value).toBeNaN();
468468
});
469+
470+
it('warns if outdated JSX transform is detected', async () => {
471+
// Warns if __self is detected, because that's only passed by a compiler
472+
expect(() => {
473+
React.createElement('div', {className: 'foo', __self: this});
474+
}).toWarnDev('Your app is using an outdated JSX transform.', {
475+
withoutStack: true,
476+
});
477+
478+
// Only warns the first time. Subsequent elements don't warn.
479+
React.createElement('div', {className: 'foo', __self: this});
480+
});
481+
482+
it('do not warn about outdated JSX transform if `key` is present', () => {
483+
// When a static "key" prop is defined _after_ a spread, the modern JSX
484+
// transform outputs `createElement` instead of `jsx`. (This is because with
485+
// `jsx`, a spread key always takes precedence over a static key, regardless
486+
// of the order, whereas `createElement` respects the order.)
487+
//
488+
// To avoid a false positive warning, we skip the warning whenever a `key`
489+
// prop is present.
490+
React.createElement('div', {key: 'foo', __self: this});
491+
});
469492
});

packages/react/src/jsx/ReactJSXElement.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ let specialPropKeyWarningShown;
3333
let specialPropRefWarningShown;
3434
let didWarnAboutStringRefs;
3535
let didWarnAboutElementRef;
36+
let didWarnAboutOldJSXRuntime;
3637

3738
if (__DEV__) {
3839
didWarnAboutStringRefs = {};
@@ -722,6 +723,27 @@ export function createElement(type, config, children) {
722723
let ref = null;
723724

724725
if (config != null) {
726+
if (__DEV__) {
727+
if (
728+
!didWarnAboutOldJSXRuntime &&
729+
'__self' in config &&
730+
// Do not assume this is the result of an oudated JSX transform if key
731+
// is present, because the modern JSX transform sometimes outputs
732+
// createElement to preserve precedence between a static key and a
733+
// spread key. To avoid false positive warnings, we never warn if
734+
// there's a key.
735+
!('key' in config)
736+
) {
737+
didWarnAboutOldJSXRuntime = true;
738+
console.warn(
739+
'Your app is using an outdated JSX transform. Update to the modern ' +
740+
'JSX transform for faster performance: ' +
741+
// TODO: Create a short link for this
742+
'https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html',
743+
);
744+
}
745+
}
746+
725747
if (hasValidRef(config)) {
726748
if (!enableRefAsProp) {
727749
ref = config.ref;

0 commit comments

Comments
 (0)