diff --git a/src/renderers/dom/shared/DOMPropertyOperations.js b/src/renderers/dom/shared/DOMPropertyOperations.js
index eee9647ef6220..6f91521eed702 100644
--- a/src/renderers/dom/shared/DOMPropertyOperations.js
+++ b/src/renderers/dom/shared/DOMPropertyOperations.js
@@ -206,6 +206,24 @@ var DOMPropertyOperations = {
}
},
+ /**
+ * Deletes an attributes from a node.
+ *
+ * @param {DOMElement} node
+ * @param {string} name
+ */
+ deleteValueForAttribute: function(node, name) {
+ node.removeAttribute(name);
+ if (__DEV__) {
+ ReactDOMInstrumentation.debugTool.onDeleteValueForProperty(node, name);
+ ReactInstrumentation.debugTool.onNativeOperation(
+ ReactDOMComponentTree.getInstanceFromNode(node)._debugID,
+ 'remove attribute',
+ name
+ );
+ }
+ },
+
/**
* Deletes the value for a property on a node.
*
diff --git a/src/renderers/dom/shared/ReactDOMComponent.js b/src/renderers/dom/shared/ReactDOMComponent.js
index 1311b05abd67a..84953d8910060 100644
--- a/src/renderers/dom/shared/ReactDOMComponent.js
+++ b/src/renderers/dom/shared/ReactDOMComponent.js
@@ -911,6 +911,13 @@ ReactDOMComponent.Mixin = {
// listener (e.g., onClick={null})
deleteListener(this, propKey);
}
+ } else if (isCustomComponent(this._tag, lastProps)) {
+ if (!RESERVED_PROPS.hasOwnProperty(propKey)) {
+ DOMPropertyOperations.deleteValueForAttribute(
+ getNode(this),
+ propKey
+ );
+ }
} else if (
DOMProperty.properties[propKey] ||
DOMProperty.isCustomAttribute(propKey)) {
diff --git a/src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js b/src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js
index dc0c53690e119..19b6fc3b4c49d 100644
--- a/src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js
+++ b/src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js
@@ -304,6 +304,15 @@ describe('ReactDOMComponent', function() {
expect(container.firstChild.className).toEqual('');
});
+ it('should properly update custom attributes on custom elements', function() {
+ var container = document.createElement('div');
+ ReactDOM.render(, container);
+ ReactDOM.render(, container);
+ var node = container.firstChild;
+ expect(node.hasAttribute('foo')).toBe(false);
+ expect(node.getAttribute('bar')).toBe('buzz');
+ });
+
it('should clear a single style prop when changing `style`', function() {
var styles = {display: 'none', color: 'red'};
var container = document.createElement('div');