From 91caa96e4261704d42333f5e02ba32d870379fc4 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Tue, 6 Feb 2024 20:03:02 -0500 Subject: [PATCH] jsx(): Treat __self and __source as normal props (#28257) These used to be reserved props because the classic React.createElement runtime passed this data as props, whereas the jsxDEV() runtime passes them as separate arguments. This brings us incrementally closer to being able to pass the props object directly through to React instead of cloning a subset into a new object. The React.createElement runtime is unaffected. --- packages/react/src/ReactElementProd.js | 16 ++++++++++++-- .../ReactElementValidator-test.internal.js | 22 +++++++++++++++++++ packages/react/src/jsx/ReactJSXElement.js | 12 ++++------ 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/packages/react/src/ReactElementProd.js b/packages/react/src/ReactElementProd.js index 88b87a6dfb..b4f1519c79 100644 --- a/packages/react/src/ReactElementProd.js +++ b/packages/react/src/ReactElementProd.js @@ -232,8 +232,14 @@ export function createElement(type, config, children) { hasOwnProperty.call(config, propName) && // Skip over reserved prop names propName !== 'key' && - // TODO: These will no longer be reserved in the next major + // TODO: `ref` will no longer be reserved in the next major propName !== 'ref' && + // ...and maybe these, too, though we currently rely on them for + // warnings and debug information in dev. Need to decide if we're OK + // with dropping them. In the jsx() runtime it's not an issue because + // the data gets passed as separate arguments instead of props, but + // it would be nice to stop relying on them entirely so we can drop + // them from the internal Fiber field. propName !== '__self' && propName !== '__source' ) { @@ -375,8 +381,14 @@ export function cloneElement(element, config, children) { hasOwnProperty.call(config, propName) && // Skip over reserved prop names propName !== 'key' && - // TODO: These will no longer be reserved in the next major + // TODO: `ref` will no longer be reserved in the next major propName !== 'ref' && + // ...and maybe these, too, though we currently rely on them for + // warnings and debug information in dev. Need to decide if we're OK + // with dropping them. In the jsx() runtime it's not an issue because + // the data gets passed as separate arguments instead of props, but + // it would be nice to stop relying on them entirely so we can drop + // them from the internal Fiber field. propName !== '__self' && propName !== '__source' ) { diff --git a/packages/react/src/__tests__/ReactElementValidator-test.internal.js b/packages/react/src/__tests__/ReactElementValidator-test.internal.js index cd1259de19..c32ddd3b3f 100644 --- a/packages/react/src/__tests__/ReactElementValidator-test.internal.js +++ b/packages/react/src/__tests__/ReactElementValidator-test.internal.js @@ -569,4 +569,26 @@ describe('ReactElementValidator', () => { React.createElement(Lazy); expect(didCall).toBe(false); }); + + it('__self and __source are treated as normal props', async () => { + // These used to be reserved props because the classic React.createElement + // runtime passed this data as props, whereas the jsxDEV() runtime passes + // them as separate arguments. + function Child({__self, __source}) { + return __self + __source; + } + + const container = document.createElement('div'); + const root = ReactDOMClient.createRoot(container); + // NOTE: The Babel transform treats the presence of these props as a syntax + // error but theoretically it doesn't have to. Using spread here to + // circumvent the syntax error and demonstrate that the runtime + // doesn't care. + const props = { + __self: 'Hello ', + __source: 'world!', + }; + await act(() => root.render()); + expect(container.textContent).toBe('Hello world!'); + }); }); diff --git a/packages/react/src/jsx/ReactJSXElement.js b/packages/react/src/jsx/ReactJSXElement.js index 849d2e1604..bc3691e4a9 100644 --- a/packages/react/src/jsx/ReactJSXElement.js +++ b/packages/react/src/jsx/ReactJSXElement.js @@ -239,10 +239,8 @@ export function jsx(type, config, maybeKey) { hasOwnProperty.call(config, propName) && // Skip over reserved prop names propName !== 'key' && - // TODO: These will no longer be reserved in the next major - propName !== 'ref' && - propName !== '__self' && - propName !== '__source' + // TODO: `ref` will no longer be reserved in the next major + propName !== 'ref' ) { props[propName] = config[propName]; } @@ -316,10 +314,8 @@ export function jsxDEV(type, config, maybeKey, source, self) { hasOwnProperty.call(config, propName) && // Skip over reserved prop names propName !== 'key' && - // TODO: These will no longer be reserved in the next major - propName !== 'ref' && - propName !== '__self' && - propName !== '__source' + // TODO: `ref` will no longer be reserved in the next major + propName !== 'ref' ) { props[propName] = config[propName]; }