Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Begin diff with next DOM sibling in forceUpdate #1689

Merged
merged 13 commits into from
Jun 10, 2019

Conversation

andrewiggins
Copy link
Member

When a Component isn't currently associated with a DOM node, we need to begin the diff from it's next DOM sibling so that if this render of the Component adds DOM, it will be correctly inserted into it's parent (instead of always appended like before).

I stole this idea from @marvinhagemeister's #1605, but re-implemented it so it no longer needs a _sibling pointer which should make this a little smaller.

I tried exploring other ideas (like two loops in diffChildren) but they didn't pan out like I had hoped (would be just as big a change and couldn't get Fragments working after a while 😞). I figured it would be better to just get something working in master first.

With #1658, I think this solution works out pretty elegantly anyway (diffChildren is still the only place where DOM is added or removed).

Total change: +75 B 😬

NOTE: depends on #1688 (+5 B) so don't merge until after #1688 is in master. I'll rebase this PR on master once that happens.

test/browser/fragments.test.js Show resolved Hide resolved

/** @jsx h */

describe('getDomSibling', () => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marvinhagemeister FYI, I copied your tests from #1605 and added a bunch. I couldn't get the implementation of getDomSibling in #1605 to pass all the new tests which is why I re-built it from scratch.

Copy link
Member

@marvinhagemeister marvinhagemeister Jun 8, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm glad you did that as my version was a bit hacked together to get a good sense of the problem space. I didn't have time to clean it up. The variant in this PR is much cleaner and the childIndex handling is ace 💯

it('should find sibling with nested placeholder', () => {
render((
<div key="0">
<Fragment key="0.0">
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added keys to some of the tests here just cuz they made it easier to debug getDomSibling with a couple of well-placed console.log(vnode.key)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohh that's a very neat way to debug ordering. I'll use that on my next PR 👍

* @param {import('./internal').VNode} vnode
* @param {number | null} [childIndex]
*/
export function getDomSibling(vnode, childIndex) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forget - does JS support optimized tail recursion? I think this function could be re-written to be tail-recursive if it isn't already (I forget what exactly qualifies as tail recursive).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

V8 had it for a while but not anymore, tracking bug: https://bugs.chromium.org/p/v8/issues/detail?id=4698

Copy link
Member

@JoviDeCroock JoviDeCroock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey Andre, super nice work! I think this solves the following issue as well: https://github.com/preactjs/preact/pull/1687/files#diff-d5abae78db533876550fd82502fe7000R186

* @param {import('./internal').VNode} vnode
* @param {number | null} [childIndex]
*/
export function getDomSibling(vnode, childIndex) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

V8 had it for a while but not anymore, tracking bug: https://bugs.chromium.org/p/v8/issues/detail?id=4698

Copy link
Member

@marvinhagemeister marvinhagemeister left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wohoo! This PR makes me really excited!! It is way better than my initial draft in #1605 👍 👍 Especially the getDomSibling function is a thing of beauty 🙌

Hopefully we've caught all remaining issues with Fragment ordering with this PR. The approach is a lot more sound compared to what's in master. Really amazing things happening here 💯 🍀

@andrewiggins andrewiggins changed the base branch from parent-pointer to master June 9, 2019 10:29
@andrewiggins andrewiggins marked this pull request as ready for review June 10, 2019 01:26
@coveralls
Copy link

coveralls commented Jun 10, 2019

Coverage Status

Coverage increased (+1.3%) to 99.778% when pulling 3688977 on forceupdate-diffChildren3 into c37cfee on master.

Copy link
Member

@marvinhagemeister marvinhagemeister left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is so good 🙌💯 Let's do it!

@marvinhagemeister marvinhagemeister merged commit 93c626e into master Jun 10, 2019
@JoviDeCroock JoviDeCroock deleted the forceupdate-diffChildren3 branch June 10, 2019 08:34
@JoviDeCroock JoviDeCroock restored the forceupdate-diffChildren3 branch June 10, 2019 08:34
@andrewiggins andrewiggins deleted the forceupdate-diffChildren3 branch June 10, 2019 14:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants