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)) {