Warn if outdated JSX transform is detected (#28781)

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.
This commit is contained in:
Andrew Clark
2024-04-09 17:13:19 -04:00
committed by GitHub
parent 3f9e237a2f
commit ed3c65caf0
3 changed files with 58 additions and 2 deletions
@@ -91,6 +91,9 @@ describe('ReactDeprecationWarnings', () => {
);
});
// Disabling this until #28732 lands so we can assert on the warning message.
// (It's already disabled in all but the Meta builds, anyway. Nbd.)
// @gate TODO || !__DEV__
// @gate !disableStringRefs
it('should warn when owner and self are the same for string refs', async () => {
class RefComponent extends React.Component {
@@ -114,8 +117,11 @@ describe('ReactDeprecationWarnings', () => {
await waitForAll([]);
});
// Disabling this until #28732 lands so we can assert on the warning message.
// (It's already disabled in all but the Meta builds, anyway. Nbd.)
// @gate TODO || !__DEV__
// @gate !disableStringRefs
it('should warn when owner and self are different for string refs', async () => {
it('should warn when owner and self are different for string refs (createElement)', async () => {
class RefComponent extends React.Component {
render() {
return null;
@@ -143,7 +149,7 @@ describe('ReactDeprecationWarnings', () => {
// @gate __DEV__
// @gate !disableStringRefs
it('should warn when owner and self are different for string refs', async () => {
it('should warn when owner and self are different for string refs (jsx)', async () => {
class RefComponent extends React.Component {
render() {
return null;
@@ -466,4 +466,31 @@ describe('ReactCreateElement', () => {
});
expect(test.props.value).toBeNaN();
});
it('warns if outdated JSX transform is detected', async () => {
// Warns if __self is detected, because that's only passed by a compiler
expect(() => {
React.createElement('div', {className: 'foo', __self: this});
}).toWarnDev(
'Your app (or one of its dependencies) is using an outdated ' +
'JSX transform.',
{
withoutStack: true,
},
);
// Only warns the first time. Subsequent elements don't warn.
React.createElement('div', {className: 'foo', __self: this});
});
it('do not warn about outdated JSX transform if `key` is present', () => {
// 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.
React.createElement('div', {key: 'foo', __self: this});
});
});
+23
View File
@@ -33,6 +33,7 @@ let specialPropKeyWarningShown;
let specialPropRefWarningShown;
let didWarnAboutStringRefs;
let didWarnAboutElementRef;
let didWarnAboutOldJSXRuntime;
if (__DEV__) {
didWarnAboutStringRefs = {};
@@ -722,6 +723,28 @@ export function createElement(type, config, children) {
let ref = null;
if (config != null) {
if (__DEV__) {
if (
!didWarnAboutOldJSXRuntime &&
'__self' in config &&
// Do not assume this is the result of an oudated JSX transform if key
// is present, because the modern JSX transform sometimes outputs
// createElement to preserve precedence between a static key and a
// spread key. To avoid false positive warnings, we never warn if
// there's a key.
!('key' in config)
) {
didWarnAboutOldJSXRuntime = true;
console.warn(
'Your app (or one of its dependencies) is using an outdated JSX ' +
'transform. Update to the modern JSX transform for ' +
'faster performance: ' +
// TODO: Create a short link for this
'https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html',
);
}
}
if (hasValidRef(config)) {
if (!enableRefAsProp) {
ref = config.ref;