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.
Andrew Clark committed
Feb 6, 2024 at 20:03 UTC
91caa96e4261704d42333f5e02ba32d870379fc4
3 files changed
+40
-10
packages/react/src/ReactElementProd.js
+14
-2
@@ -232,8 +232,14 @@ export function createElement(type, config, children) {
232
hasOwnProperty.call(config, propName) &&
233
// Skip over reserved prop names
234
propName !== 'key' &&
235
- // TODO: These will no longer be reserved in the next major
235
+ // TODO: `ref` will no longer be reserved in the next major
236
propName !== 'ref' &&
237
+ // ...and maybe these, too, though we currently rely on them for
238
+ // warnings and debug information in dev. Need to decide if we're OK
239
+ // with dropping them. In the jsx() runtime it's not an issue because
240
+ // the data gets passed as separate arguments instead of props, but
241
+ // it would be nice to stop relying on them entirely so we can drop
242
+ // them from the internal Fiber field.
243
propName !== '__self' &&
244
propName !== '__source'
245
) {
@@ -375,8 +381,14 @@ export function cloneElement(element, config, children) {
381
hasOwnProperty.call(config, propName) &&
382
// Skip over reserved prop names
383
propName !== 'key' &&
378
- // TODO: These will no longer be reserved in the next major
384
+ // TODO: `ref` will no longer be reserved in the next major
385
propName !== 'ref' &&
386
+ // ...and maybe these, too, though we currently rely on them for
387
+ // warnings and debug information in dev. Need to decide if we're OK
388
+ // with dropping them. In the jsx() runtime it's not an issue because
389
+ // the data gets passed as separate arguments instead of props, but
390
+ // it would be nice to stop relying on them entirely so we can drop
391
+ // them from the internal Fiber field.
392
propName !== '__self' &&
393
propName !== '__source'
394
) {
packages/react/src/__tests__/ReactElementValidator-test.internal.js
+22
@@ -569,4 +569,26 @@ describe('ReactElementValidator', () => {
569
React.createElement(Lazy);
570
expect(didCall).toBe(false);
571
});
572
+
573
+ it('__self and __source are treated as normal props', async () => {
574
+ // These used to be reserved props because the classic React.createElement
575
+ // runtime passed this data as props, whereas the jsxDEV() runtime passes
576
+ // them as separate arguments.
577
+ function Child({__self, __source}) {
578
+ return __self + __source;
579
+ }
580
+
581
+ const container = document.createElement('div');
582
+ const root = ReactDOMClient.createRoot(container);
583
+ // NOTE: The Babel transform treats the presence of these props as a syntax
584
+ // error but theoretically it doesn't have to. Using spread here to
585
+ // circumvent the syntax error and demonstrate that the runtime
586
+ // doesn't care.
587
+ const props = {
588
+ __self: 'Hello ',
589
+ __source: 'world!',
590
+ };
591
+ await act(() => root.render(<Child {...props} />));
592
+ expect(container.textContent).toBe('Hello world!');
593
+ });
594
});
packages/react/src/jsx/ReactJSXElement.js
+4
-8
@@ -239,10 +239,8 @@ export function jsx(type, config, maybeKey) {
239
hasOwnProperty.call(config, propName) &&
240
// Skip over reserved prop names
241
propName !== 'key' &&
242
- // TODO: These will no longer be reserved in the next major
243
- propName !== 'ref' &&
244
- propName !== '__self' &&
245
- propName !== '__source'
242
+ // TODO: `ref` will no longer be reserved in the next major
243
+ propName !== 'ref'
244
) {
245
props[propName] = config[propName];
246
}
@@ -316,10 +314,8 @@ export function jsxDEV(type, config, maybeKey, source, self) {
314
hasOwnProperty.call(config, propName) &&
315
// Skip over reserved prop names
316
propName !== 'key' &&
319
- // TODO: These will no longer be reserved in the next major
320
- propName !== 'ref' &&
321
- propName !== '__self' &&
322
- propName !== '__source'
317
+ // TODO: `ref` will no longer be reserved in the next major
318
+ propName !== 'ref'
319
) {
320
props[propName] = config[propName];
321
}