From d2fe50be6e5fd5dbd9d71e477f0eb3c41d2fa2e6 Mon Sep 17 00:00:00 2001 From: Joey Arhar Date: Tue, 28 Dec 2021 09:21:16 -0800 Subject: [PATCH] Allow functions to be passed to custom element setters 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 28776b1eb62fb..99f92cba9b36b 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 71b53d703c6d4..f3d6fdac89795 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)) {