From a87adefecdff672d5261e93945c227ad72c5444c Mon Sep 17 00:00:00 2001 From: Joey Arhar Date: Wed, 12 Jan 2022 13:12:07 -0700 Subject: [PATCH] Allow functions to be passed to custom element setters (#23042) This is part of the new custom element features that were implemented here: https://github.com/facebook/react/commit/24dd07bd269590ee5024b7f0f1906887d256ea86 When a custom element has a setter for a property and passes the `in` heuristic, the value passed to the react property should be assigned directly to the custom element's property, regardless of the type of the value. However, it was discovered that this isn't working with functions. This patch makes it work with functions. Fixes https://github.com/facebook/react/issues/23041 --- .../__tests__/DOMPropertyOperations-test.js | 29 +++++++++++++++++++ .../src/client/DOMPropertyOperations.js | 8 ++--- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/packages/react-dom/src/__tests__/DOMPropertyOperations-test.js b/packages/react-dom/src/__tests__/DOMPropertyOperations-test.js index 28776b1eb6..99f92cba9b 100644 --- a/packages/react-dom/src/__tests__/DOMPropertyOperations-test.js +++ b/packages/react-dom/src/__tests__/DOMPropertyOperations-test.js @@ -504,6 +504,35 @@ describe('DOMPropertyOperations', () => { expect(customElement.foo).toBe('two'); expect(customElement.getAttribute('foo')).toBe('one'); }); + + // @gate enableCustomElementPropertySupport + it('custom element properties should accept functions', () => { + const container = document.createElement('div'); + document.body.appendChild(container); + ReactDOM.render(, container); + const customElement = container.querySelector('my-custom-element'); + + // Install a setter to activate the `in` heuristic + Object.defineProperty(customElement, 'foo', { + set: function(x) { + this._foo = x; + }, + get: function() { + return this._foo; + }, + }); + function myFunction() { + return 'this is myFunction'; + } + ReactDOM.render(, container); + expect(customElement.foo).toBe(myFunction); + + // Also remove and re-add the property for good measure + ReactDOM.render(, container); + expect(customElement.foo).toBe(null); + ReactDOM.render(, container); + expect(customElement.foo).toBe(myFunction); + }); }); describe('deleteValueForProperty', () => { diff --git a/packages/react-dom/src/client/DOMPropertyOperations.js b/packages/react-dom/src/client/DOMPropertyOperations.js index 71b53d703c..f3d6fdac89 100644 --- a/packages/react-dom/src/client/DOMPropertyOperations.js +++ b/packages/react-dom/src/client/DOMPropertyOperations.js @@ -184,10 +184,6 @@ export function setValueForProperty( } } - if (shouldRemoveAttribute(name, value, propertyInfo, isCustomComponentTag)) { - value = null; - } - if ( enableCustomElementPropertySupport && isCustomComponentTag && @@ -197,6 +193,10 @@ export function setValueForProperty( return; } + if (shouldRemoveAttribute(name, value, propertyInfo, isCustomComponentTag)) { + value = null; + } + // If the prop isn't in the special list, treat it as a simple attribute. if (isCustomComponentTag || propertyInfo === null) { if (isAttributeNameSafe(name)) {