Repository: facebook/react
Pull request: jsx(): Treat __self and __source as normal props

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.

Changed files:
filename: packages/react/src/ReactElementProd.js
patch: @@ -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'
       ) {

filename: packages/react/src/__tests__/ReactElementValidator-test.internal.js
patch: @@ -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(<Child {...props} />));
+    expect(container.textContent).toBe('Hello world!');
+  });
 });

filename: packages/react/src/jsx/ReactJSXElement.js
patch: @@ -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];
       }