From 19d104df922178f8e0a3a29977d0510d6ebfb888 Mon Sep 17 00:00:00 2001 From: JSap0914 Date: Wed, 17 Jun 2026 21:45:31 +0900 Subject: [PATCH 1/2] fix(compat): avoid mutating user-provided style objects when appending px MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit handleDomVNode() was directly mutating the style object passed by the user via value[key] += 'px'. This permanently modifies the caller's original object—an unexpected side effect. Fix by shallow-copying the style object before the per-key px loop. The existing px-append behaviour is preserved; the user's object is left unchanged. --- compat/src/render.js | 1 + compat/test/browser/render.test.jsx | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/compat/src/render.js b/compat/src/render.js index 5bdcd31d4a..b45d9da7e6 100644 --- a/compat/src/render.js +++ b/compat/src/render.js @@ -136,6 +136,7 @@ function handleDomVNode(vnode) { } if (i === 'style' && typeof value === 'object') { + value = assign({}, value); for (let key in value) { if (typeof value[key] === 'number' && !IS_NON_DIMENSIONAL.test(key)) { value[key] += 'px'; diff --git a/compat/test/browser/render.test.jsx b/compat/test/browser/render.test.jsx index 64be111d1b..680768c4cc 100644 --- a/compat/test/browser/render.test.jsx +++ b/compat/test/browser/render.test.jsx @@ -886,4 +886,21 @@ describe('compat render', () => { } } }); + + it('should not mutate the original style object when appending px', () => { + const style = { margin: 10, opacity: 0.5 }; + const originalMargin = style.margin; + const originalOpacity = style.opacity; + + render(
, scratch); + + expect(style.margin).to.equal( + originalMargin, + 'style.margin should not be mutated' + ); + expect(style.opacity).to.equal( + originalOpacity, + 'style.opacity should not be mutated' + ); + }); }); From ef09510fada7020e399e4f8a0c60b7d60bf38d2a Mon Sep 17 00:00:00 2001 From: JSap0914 Date: Thu, 18 Jun 2026 04:00:02 +0900 Subject: [PATCH 2/2] address review: clone style lazily only when appending px --- compat/src/render.js | 6 +++++- compat/test/browser/render.test.jsx | 12 +----------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/compat/src/render.js b/compat/src/render.js index b45d9da7e6..b0ac27aa56 100644 --- a/compat/src/render.js +++ b/compat/src/render.js @@ -136,9 +136,13 @@ function handleDomVNode(vnode) { } if (i === 'style' && typeof value === 'object') { - value = assign({}, value); + let cloned = false; for (let key in value) { if (typeof value[key] === 'number' && !IS_NON_DIMENSIONAL.test(key)) { + if (!cloned) { + value = assign({}, value); + cloned = true; + } value[key] += 'px'; } } diff --git a/compat/test/browser/render.test.jsx b/compat/test/browser/render.test.jsx index 680768c4cc..5968d6e3a1 100644 --- a/compat/test/browser/render.test.jsx +++ b/compat/test/browser/render.test.jsx @@ -889,18 +889,8 @@ describe('compat render', () => { it('should not mutate the original style object when appending px', () => { const style = { margin: 10, opacity: 0.5 }; - const originalMargin = style.margin; - const originalOpacity = style.opacity; - render(
, scratch); - expect(style.margin).to.equal( - originalMargin, - 'style.margin should not be mutated' - ); - expect(style.opacity).to.equal( - originalOpacity, - 'style.opacity should not be mutated' - ); + expect(style).to.deep.equal({ margin: 10, opacity: 0.5 }); }); });