From bf6abc26a6275e9c982bcef1957f06714e7e1179 Mon Sep 17 00:00:00 2001 From: cristianbote Date: Wed, 2 Oct 2019 13:03:52 +0300 Subject: [PATCH 01/15] (fix) Fix the href not getting removed (#1943) --- src/diff/props.js | 2 ++ test/browser/render.test.js | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/src/diff/props.js b/src/diff/props.js index e6f0a69084a..7cda14cd2f9 100644 --- a/src/diff/props.js +++ b/src/diff/props.js @@ -89,6 +89,8 @@ function setProperty(dom, name, value, oldValue, isSvg) { } else if ( name!=='list' + // The href should be removed via `.setProperty` + && name!=='href' && name!=='tagName' // HTMLButtonElement.form and HTMLInputElement.form are read-only but can be set using // setAttribute diff --git a/test/browser/render.test.js b/test/browser/render.test.js index be955b2b49a..4b14852b4e3 100644 --- a/test/browser/render.test.js +++ b/test/browser/render.test.js @@ -999,6 +999,13 @@ describe('render()', () => { expect(scratch.innerHTML).to.equal('
foo
'); }); + it('should remove missing href attributes', () => { + render(, scratch); + render(, scratch); + + expect(scratch.firstElementChild.hasAttribute('href')).to.be.false; + }); + // see preact/#1327 it('should not reuse unkeyed components', () => { class X extends Component { From 419d23c8d6d1c15422252cb67b310a5ac1d049ec Mon Sep 17 00:00:00 2001 From: cristianbote Date: Thu, 3 Oct 2019 09:13:28 +0300 Subject: [PATCH 02/15] Switch to undefined check --- src/diff/props.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/diff/props.js b/src/diff/props.js index 7cda14cd2f9..2af905a5304 100644 --- a/src/diff/props.js +++ b/src/diff/props.js @@ -14,7 +14,7 @@ export function diffProps(dom, newProps, oldProps, isSvg, hydrate) { let i; for (i in oldProps) { - if (!(i in newProps)) { + if (typeof newProps[i] === 'undefined') { setProperty(dom, i, null, oldProps[i], isSvg); } } @@ -89,16 +89,15 @@ function setProperty(dom, name, value, oldValue, isSvg) { } else if ( name!=='list' - // The href should be removed via `.setProperty` - && name!=='href' && name!=='tagName' // HTMLButtonElement.form and HTMLInputElement.form are read-only but can be set using // setAttribute && name!=='form' + && value!=null && !isSvg && (name in dom) ) { - dom[name] = value==null ? '' : value; + dom[name] = value; } else if (typeof value!=='function' && name!=='dangerouslySetInnerHTML') { if (name!==(name = name.replace(/^xlink:?/, ''))) { From aca4d2c402e6fcfa6d3c20da82dcd2d210fe1945 Mon Sep 17 00:00:00 2001 From: cristianbote Date: Thu, 3 Oct 2019 11:18:33 +0300 Subject: [PATCH 03/15] Update test case to be more relevant --- test/browser/render.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/browser/render.test.js b/test/browser/render.test.js index 7dd71bdca97..d4614b09fad 100644 --- a/test/browser/render.test.js +++ b/test/browser/render.test.js @@ -999,8 +999,8 @@ describe('render()', () => { expect(scratch.innerHTML).to.equal('
foo
'); }); - it('should remove missing href attributes', () => { - render(
, scratch); + it('should remove old attributes that are undefined', () => { + render(, scratch); render(, scratch); expect(scratch.firstElementChild.hasAttribute('href')).to.be.false; From b996af1236de6c90b48b5bb0621925403b45ad09 Mon Sep 17 00:00:00 2001 From: cristianbote Date: Thu, 3 Oct 2019 14:54:12 +0300 Subject: [PATCH 04/15] Remove test cases for border --- test/browser/render.test.js | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/test/browser/render.test.js b/test/browser/render.test.js index d4614b09fad..659a2939842 100644 --- a/test/browser/render.test.js +++ b/test/browser/render.test.js @@ -282,33 +282,6 @@ describe('render()', () => { expect(scratch.firstChild.value).to.equal('0.5'); }); - // IE or IE Edge will throw when attribute values don't conform to the - // spec. That's the correct behaviour, but bad for this test... - if (!/(Edge|MSIE|Trident)/.test(navigator.userAgent)) { - it('should not clear falsy DOM properties', () => { - function test(val) { - render(( -
- - - - ), scratch); - } - - test('2'); - test(false); - expect(scratch.innerHTML).to.equal('
', 'for false'); - - test('3'); - test(null); - expect(scratch.innerHTML).to.equal('
', 'for null'); - - test('4'); - test(undefined); - expect(scratch.innerHTML).to.equal('
', 'for undefined'); - }); - } - // Test for preactjs/preact#651 it('should set enumerable boolean attribute', () => { render(, scratch); From 6851acddd2a1ed6e2856da9259c67e953f48ceb5 Mon Sep 17 00:00:00 2001 From: cristianbote Date: Thu, 3 Oct 2019 14:57:47 +0300 Subject: [PATCH 05/15] Actually keep the test and update the expect value --- test/browser/render.test.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/test/browser/render.test.js b/test/browser/render.test.js index 659a2939842..c805d924ced 100644 --- a/test/browser/render.test.js +++ b/test/browser/render.test.js @@ -282,6 +282,33 @@ describe('render()', () => { expect(scratch.firstChild.value).to.equal('0.5'); }); + // IE or IE Edge will throw when attribute values don't conform to the + // spec. That's the correct behaviour, but bad for this test... + if (!/(Edge|MSIE|Trident)/.test(navigator.userAgent)) { + it('should not clear falsy DOM properties', () => { + function test(val) { + render(( +
+ + + + ), scratch); + } + + test('2'); + test(false); + expect(scratch.innerHTML).to.equal('
', 'for false'); + + test('3'); + test(null); + expect(scratch.innerHTML).to.equal('
', 'for null'); + + test('4'); + test(undefined); + expect(scratch.innerHTML).to.equal('
', 'for undefined'); + }); + } + // Test for preactjs/preact#651 it('should set enumerable boolean attribute', () => { render(, scratch); From 5711dc9f7a2040400810e0be0a3f6adbabf74b5a Mon Sep 17 00:00:00 2001 From: cristianbote Date: Thu, 3 Oct 2019 22:22:01 +0300 Subject: [PATCH 06/15] Revert the if case and added the attribute removal as well --- src/diff/props.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/diff/props.js b/src/diff/props.js index 2af905a5304..9b1ae1659b0 100644 --- a/src/diff/props.js +++ b/src/diff/props.js @@ -93,11 +93,16 @@ function setProperty(dom, name, value, oldValue, isSvg) { // HTMLButtonElement.form and HTMLInputElement.form are read-only but can be set using // setAttribute && name!=='form' - && value!=null && !isSvg && (name in dom) ) { - dom[name] = value; + if (value==null) { + dom.removeAttribute(name); + dom[name] = ''; + } + else { + dom[name] = value; + } } else if (typeof value!=='function' && name!=='dangerouslySetInnerHTML') { if (name!==(name = name.replace(/^xlink:?/, ''))) { From 686181a63c4d7330c26bce3920e0a5631cce1015 Mon Sep 17 00:00:00 2001 From: Marvin Hagemeister Date: Mon, 7 Oct 2019 21:47:04 +0200 Subject: [PATCH 07/15] sCU shouldn't block forceUpdate from child (#1984) The way MobX works is that every observed component returns false from shouldComponentUpdate unless props have changed. They than trigger the child renders themselves via direct forceUpdate calls. Our issue was that we only set the force flag sort-of globally during a render and would ignore any children that have been enqueued via forceUpdate in the same render cycle. With these changes we ensure that the force flag is always the one from the current component. --- src/component.js | 8 ++-- src/diff/children.js | 2 +- src/diff/index.js | 6 +-- src/render.js | 1 - .../lifecycles/shouldComponentUpdate.test.js | 39 +++++++++++++++++++ 5 files changed, 47 insertions(+), 9 deletions(-) diff --git a/src/component.js b/src/component.js index eef44498f90..a813eb10e84 100644 --- a/src/component.js +++ b/src/component.js @@ -123,13 +123,13 @@ export function getDomSibling(vnode, childIndex) { function renderComponent(component) { let vnode = component._vnode, oldDom = vnode._dom, - parentDom = component._parentDom, - force = component._force; - component._force = false; + parentDom = component._parentDom; + if (parentDom) { let mounts = []; - let newDom = diff(parentDom, vnode, assign({}, vnode), component._context, parentDom.ownerSVGElement!==undefined, null, mounts, force, oldDom == null ? getDomSibling(vnode) : oldDom); + let newDom = diff(parentDom, vnode, assign({}, vnode), component._context, parentDom.ownerSVGElement!==undefined, null, mounts, oldDom == null ? getDomSibling(vnode) : oldDom); commitRoot(mounts, vnode); + component._force = false; if (newDom != oldDom) { updateParentDomPointers(vnode); diff --git a/src/diff/children.js b/src/diff/children.js index 106b909a876..93c772d3d49 100644 --- a/src/diff/children.js +++ b/src/diff/children.js @@ -82,7 +82,7 @@ export function diffChildren(parentDom, newParentVNode, oldParentVNode, context, oldVNode = oldVNode || EMPTY_OBJ; // Morph the old element into the new one, but don't append it to the dom yet - newDom = diff(parentDom, childVNode, oldVNode, context, isSvg, excessDomChildren, mounts, null, oldDom, isHydrating); + newDom = diff(parentDom, childVNode, oldVNode, context, isSvg, excessDomChildren, mounts, oldDom, isHydrating); if ((j = childVNode.ref) && oldVNode.ref != j) { (refs || (refs=[])).push(j, childVNode._component || newDom, childVNode); diff --git a/src/diff/index.js b/src/diff/index.js index 533339807a3..01d7648074f 100644 --- a/src/diff/index.js +++ b/src/diff/index.js @@ -22,7 +22,7 @@ import options from '../options'; * Fragments that have siblings. In most cases, it starts out as `oldChildren[0]._dom`. * @param {boolean} isHydrating Whether or not we are in hydration */ -export function diff(parentDom, newVNode, oldVNode, context, isSvg, excessDomChildren, mounts, force, oldDom, isHydrating) { +export function diff(parentDom, newVNode, oldVNode, context, isSvg, excessDomChildren, mounts, oldDom, isHydrating) { let tmp, newType = newVNode.type; // When passing through createElement it assigns the object @@ -81,11 +81,11 @@ export function diff(parentDom, newVNode, oldVNode, context, isSvg, excessDomChi if (c.componentDidMount!=null) mounts.push(c); } else { - if (newType.getDerivedStateFromProps==null && force==null && c.componentWillReceiveProps!=null) { + if (newType.getDerivedStateFromProps==null && c._force==null && c.componentWillReceiveProps!=null) { c.componentWillReceiveProps(newProps, cctx); } - if (!force && c.shouldComponentUpdate!=null && c.shouldComponentUpdate(newProps, c._nextState, cctx)===false) { + if (!c._force && c.shouldComponentUpdate!=null && c.shouldComponentUpdate(newProps, c._nextState, cctx)===false) { c.props = newProps; c.state = c._nextState; c._dirty = false; diff --git a/src/render.js b/src/render.js index 0b61802f6af..23b3c1c7bbd 100644 --- a/src/render.js +++ b/src/render.js @@ -33,7 +33,6 @@ export function render(vnode, parentDom, replaceNode) { ? null : EMPTY_ARR.slice.call(parentDom.childNodes), mounts, - false, replaceNode || EMPTY_OBJ, isHydrating, ); diff --git a/test/browser/lifecycles/shouldComponentUpdate.test.js b/test/browser/lifecycles/shouldComponentUpdate.test.js index 5f72d9ee8f4..a5df38aa5ed 100644 --- a/test/browser/lifecycles/shouldComponentUpdate.test.js +++ b/test/browser/lifecycles/shouldComponentUpdate.test.js @@ -154,6 +154,45 @@ describe('Lifecycle methods', () => { expect(Foo.prototype.render).to.have.been.calledTwice; }); + it('should not block queued child forceUpdate', () => { + let i = 0; + let updateInner; + class Inner extends Component { + shouldComponentUpdate() { + return i===0; + } + render() { + updateInner = () => this.forceUpdate(); + return
{++i}
; + } + } + + let updateOuter; + class Outer extends Component { + shouldComponentUpdate() { + return i===0; + } + render() { + updateOuter = () => this.forceUpdate(); + return ; + } + } + + class App extends Component { + render() { + return ; + } + } + + render(, scratch); + + updateOuter(); + updateInner(); + rerender(); + + expect(scratch.textContent).to.equal('2'); + }); + it('should be passed next props and state', () => { /** @type {() => void} */ From 1583c4e8b0a7a21aca965f6b0253174a76b1a71d Mon Sep 17 00:00:00 2001 From: Jovi De Croock Date: Mon, 7 Oct 2019 22:12:24 +0200 Subject: [PATCH 08/15] (fix) - should reorder memoized children (#1980) --- src/diff/index.js | 2 +- .../lifecycles/shouldComponentUpdate.test.js | 36 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/diff/index.js b/src/diff/index.js index 01d7648074f..2bb122f102e 100644 --- a/src/diff/index.js +++ b/src/diff/index.js @@ -90,7 +90,7 @@ export function diff(parentDom, newVNode, oldVNode, context, isSvg, excessDomChi c.state = c._nextState; c._dirty = false; c._vnode = newVNode; - newVNode._dom = oldDom!=null ? oldDom!==oldVNode._dom ? oldDom : oldVNode._dom : null; + newVNode._dom = oldVNode._dom; newVNode._children = oldVNode._children; for (tmp = 0; tmp < newVNode._children.length; tmp++) { if (newVNode._children[tmp]) newVNode._children[tmp]._parent = newVNode; diff --git a/test/browser/lifecycles/shouldComponentUpdate.test.js b/test/browser/lifecycles/shouldComponentUpdate.test.js index a5df38aa5ed..5560272dee8 100644 --- a/test/browser/lifecycles/shouldComponentUpdate.test.js +++ b/test/browser/lifecycles/shouldComponentUpdate.test.js @@ -63,6 +63,42 @@ describe('Lifecycle methods', () => { expect(ShouldNot.prototype.render).to.have.been.calledOnce; }); + it('should reorder non-updating children', () => { + const rows = [ + { id: '1', a: 5, b: 100 }, + { id: '2', a: 50, b: 10 }, + { id: '3', a: 25, b: 1000 } + ]; + + class Row extends Component { + shouldComponentUpdate(nextProps) { + return nextProps.id !== this.props.id; + } + + render() { + return this.props.id; + } + } + + const App = ({ sortBy }) => ( +
+ + {rows + .sort((a, b) => (a[sortBy] > b[sortBy] ? -1 : 1)) + .map(row => ( + + ))} +
+
+ ); + + render(, scratch); + expect(scratch.innerHTML).to.equal('
231
'); + + render(, scratch); + expect(scratch.innerHTML).to.equal('
312
'); + }); + it('should rerender when sCU returned false before', () => { let c; let spy = sinon.spy(); From 90ad88602ad52ce371a6b77d60e0fcbb76fa712e Mon Sep 17 00:00:00 2001 From: Den Ilin Date: Tue, 8 Oct 2019 12:49:18 +0300 Subject: [PATCH 09/15] Add Songsterr to Real-World Apps section (#1972) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f10e14662c4..dfbfdf6bdff 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ Preact supports modern browsers and IE9+: - [**Preact Hacker News**](https://hn.kristoferbaxter.com) _([GitHub Project](https://github.com/kristoferbaxter/preact-hn))_ - [**Play.cash**](https://play.cash) :notes: _([GitHub Project](https://github.com/feross/play.cash))_ +- [**Songsterr**](https://www.songsterr.com)Ā šŸŽ¼Ā UsingĀ PreactĀ XĀ inĀ productionĀ sinceĀ 10.0Ā alpha - [**BitMidi**](https://bitmidi.com/) šŸŽ¹ Wayback machine for free MIDI files _([GitHub Project](https://github.com/feross/bitmidi.com))_ - [**Ultimate Guitar**](https://www.ultimate-guitar.com) šŸŽøspeed boosted by Preact. - [**ESBench**](http://esbench.com) is built using Preact. From d4bf46d84f2ad221211227695302f3b45c2e92c8 Mon Sep 17 00:00:00 2001 From: Nathan Wong Date: Tue, 8 Oct 2019 12:56:05 -0700 Subject: [PATCH 10/15] Append portal node to container instead of prepend (#1971) * Append portal node to container instead of prepend This fixes the issue described in #1806. Expected test outcomes were generated by taking the test code and running it with React instead of Preact. * Add additional test for portals testing ordering --- compat/src/index.js | 4 +- compat/test/browser/portals.test.js | 60 +++++++++++++++++++++-------- 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/compat/src/index.js b/compat/src/index.js index 99be2bb49b2..aa8b9153b4f 100644 --- a/compat/src/index.js +++ b/compat/src/index.js @@ -99,8 +99,8 @@ function Portal(props) { // Hydrate existing nodes to keep the dom intact, when rendering // wrap into the container. hydrate('', container); - // Insert before first child (will just append if firstChild is null). - container.insertBefore(_this._temp, container.firstChild); + // Append to the container (this matches React's behavior) + container.appendChild(_this._temp); // At this point we have mounted and should set our container. _this._hasMounted = true; _this._container = container; diff --git a/compat/test/browser/portals.test.js b/compat/test/browser/portals.test.js index 00343b23bf9..db6832ad6b8 100644 --- a/compat/test/browser/portals.test.js +++ b/compat/test/browser/portals.test.js @@ -76,7 +76,7 @@ describe('Portal', () => { toggle(); rerender(); - expect(scratch.innerHTML).to.equal('
foobar

Hello

'); + expect(scratch.innerHTML).to.equal('

Hello

foobar
'); }); it('should notice prop changes on the portal', () => { @@ -204,15 +204,15 @@ describe('Portal', () => { toggle(); rerender(); - expect(scratch.innerHTML).to.equal('
foobar

Hello

'); + expect(scratch.innerHTML).to.equal('

Hello

foobar
'); toggle2(); rerender(); - expect(scratch.innerHTML).to.equal('
foobar
'); + expect(scratch.innerHTML).to.equal('
foobar
'); toggle2(); rerender(); - expect(scratch.innerHTML).to.equal('
foobar

Hello

'); + expect(scratch.innerHTML).to.equal('

Hello

foobar
'); toggle(); rerender(); @@ -245,15 +245,15 @@ describe('Portal', () => { toggle(); rerender(); - expect(scratch.innerHTML).to.equal('
foobar

Hello

'); + expect(scratch.innerHTML).to.equal('

Hello

foobar
'); toggle2(); rerender(); - expect(scratch.innerHTML).to.equal('
foobar2
foobar

Hello

'); + expect(scratch.innerHTML).to.equal('

Hello

foobar
foobar2
'); toggle2(); rerender(); - expect(scratch.innerHTML).to.equal('
foobar

Hello

'); + expect(scratch.innerHTML).to.equal('

Hello

foobar
'); toggle(); rerender(); @@ -280,7 +280,7 @@ describe('Portal', () => { set(() => ref); rerender(); - expect(scratch.innerHTML).to.equal('
foobar

Hello

'); + expect(scratch.innerHTML).to.equal('

Hello

foobar
'); }); it('should work with replacing placeholder portals', () => { @@ -305,7 +305,7 @@ describe('Portal', () => { toggle(); rerender(); - expect(scratch.innerHTML).to.equal('
foobar

Hello

'); + expect(scratch.innerHTML).to.equal('

Hello

foobar
'); toggle(); rerender(); @@ -313,7 +313,7 @@ describe('Portal', () => { toggle2(); rerender(); - expect(scratch.innerHTML).to.equal('
foobar

Hello

'); + expect(scratch.innerHTML).to.equal('

Hello

foobar
'); toggle2(); rerender(); @@ -340,7 +340,7 @@ describe('Portal', () => { toggle(); rerender(); - expect(scratch.innerHTML).to.equal('
foobar
foobar

Hello

'); + expect(scratch.innerHTML).to.equal('
foobar

Hello

foobar
'); }); it('should support nested portals', () => { @@ -374,11 +374,11 @@ describe('Portal', () => { toggle(); rerender(); - expect(scratch.innerHTML).to.equal('

Inner

Hello

'); + expect(scratch.innerHTML).to.equal('

Hello

Inner

'); toggle2(); rerender(); - expect(scratch.innerHTML).to.equal('

hiFromBar

innerPortal

Inner

Hello

'); + expect(scratch.innerHTML).to.equal('

Hello

Inner

innerPortal

hiFromBar

'); toggle(); rerender(); @@ -429,7 +429,7 @@ describe('Portal', () => { expect(spy).to.be.calledOnce; }); - it('should switch between non portal and portal node', () => { + it('should switch between non portal and portal node (Modal as lastChild)', () => { let toggle; const Modal = ({ children, open }) => open ? createPortal(
{children}
, scratch) @@ -452,6 +452,36 @@ describe('Portal', () => { toggle(); rerender(); - expect(scratch.innerHTML).to.equal('
Hello
Open
'); + expect(scratch.innerHTML).to.equal('
Open
Hello
'); + }); + + it('should switch between non portal and portal node (Modal as firstChild)', () => { + let toggle; + const Modal = ({ children, open }) => open + ? createPortal(
{children}
, scratch) + :
Closed
; + + const App = () => { + const [open, setOpen] = useState(false); + toggle = setOpen.bind(this, (x) => !x); + return ( +
+ Hello + + {open ? 'Open' : 'Closed'} +
+ ); + }; + + render(, scratch); + expect(scratch.innerHTML).to.equal('
Closed
Closed
'); + + toggle(); + rerender(); + expect(scratch.innerHTML).to.equal('
Open
Hello
'); + + toggle(); + rerender(); + expect(scratch.innerHTML).to.equal('
Closed
Closed
'); }); }); From 10a1d0a4daaffa6ff57cb90a2c4826ab3587fdfe Mon Sep 17 00:00:00 2001 From: cristianbote Date: Tue, 8 Oct 2019 23:45:56 +0300 Subject: [PATCH 11/15] Skipped the removal for value and checked --- src/diff/props.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/diff/props.js b/src/diff/props.js index 9b1ae1659b0..45d67aa4644 100644 --- a/src/diff/props.js +++ b/src/diff/props.js @@ -14,7 +14,7 @@ export function diffProps(dom, newProps, oldProps, isSvg, hydrate) { let i; for (i in oldProps) { - if (typeof newProps[i] === 'undefined') { + if (newProps[i] === undefined) { setProperty(dom, i, null, oldProps[i], isSvg); } } @@ -97,8 +97,10 @@ function setProperty(dom, name, value, oldValue, isSvg) { && (name in dom) ) { if (value==null) { - dom.removeAttribute(name); - dom[name] = ''; + if (name!=='value' && name!=='checked') { + dom[name] = ''; + dom.removeAttribute(name); + } } else { dom[name] = value; From 125410db27d3c02865b3ff66e342279cb509c00f Mon Sep 17 00:00:00 2001 From: cristianbote Date: Tue, 8 Oct 2019 23:48:51 +0300 Subject: [PATCH 12/15] Added doc comments --- src/diff/props.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/diff/props.js b/src/diff/props.js index 45d67aa4644..a88720cead1 100644 --- a/src/diff/props.js +++ b/src/diff/props.js @@ -97,8 +97,12 @@ function setProperty(dom, name, value, oldValue, isSvg) { && (name in dom) ) { if (value==null) { + // If we're not dealing with a input's value or checked attribute if (name!=='value' && name!=='checked') { + // Set the value to empty string first dom[name] = ''; + + // And remove the attribute entirely dom.removeAttribute(name); } } From be852af47bc2389df3a9d15c6f07b5cebff9bba3 Mon Sep 17 00:00:00 2001 From: cristianbote Date: Tue, 8 Oct 2019 23:56:29 +0300 Subject: [PATCH 13/15] Reverted component change --- src/component.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/component.js b/src/component.js index a813eb10e84..df847c58ae3 100644 --- a/src/component.js +++ b/src/component.js @@ -129,7 +129,6 @@ function renderComponent(component) { let mounts = []; let newDom = diff(parentDom, vnode, assign({}, vnode), component._context, parentDom.ownerSVGElement!==undefined, null, mounts, oldDom == null ? getDomSibling(vnode) : oldDom); commitRoot(mounts, vnode); - component._force = false; if (newDom != oldDom) { updateParentDomPointers(vnode); From 18cb89561e629aaf825817a238ad4a3173bcd616 Mon Sep 17 00:00:00 2001 From: cristianbote Date: Wed, 9 Oct 2019 00:04:07 +0300 Subject: [PATCH 14/15] Added a better doc line --- src/diff/props.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/diff/props.js b/src/diff/props.js index a88720cead1..4cbede26a02 100644 --- a/src/diff/props.js +++ b/src/diff/props.js @@ -97,7 +97,7 @@ function setProperty(dom, name, value, oldValue, isSvg) { && (name in dom) ) { if (value==null) { - // If we're not dealing with a input's value or checked attribute + // This condition here guards against modifying a unonctrolled input's value if (name!=='value' && name!=='checked') { // Set the value to empty string first dom[name] = ''; From 0c3c03ae67466f9143b117f4e1000c0e85c122d8 Mon Sep 17 00:00:00 2001 From: cristianbote Date: Wed, 16 Oct 2019 22:27:57 +0300 Subject: [PATCH 15/15] Handle the class attribute removal --- src/diff/index.js | 2 +- src/diff/props.js | 2 +- test/browser/render.test.js | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/diff/index.js b/src/diff/index.js index 88557ff0860..7e57b6c301a 100644 --- a/src/diff/index.js +++ b/src/diff/index.js @@ -240,7 +240,7 @@ function diffElementNodes(dom, newVNode, oldVNode, context, isSvg, excessDomChil if (oldProps === EMPTY_OBJ) { oldProps = {}; for (let i=0; i { ); render(, scratch); - expect(scratch.innerHTML).to.equal('
Bye
'); + expect(scratch.innerHTML).to.equal('
Bye
'); }); it('should remove class attributes', () => { @@ -495,7 +495,7 @@ describe('render()', () => { expect(scratch.innerHTML).to.equal('
Bye
'); render(, scratch); - expect(scratch.innerHTML).to.equal('
Bye
'); + expect(scratch.innerHTML).to.equal('
Bye
'); }); it('should remove old styles', () => {