Skip to content

Commit 3eccf5b

Browse files
authored
Fix async-loader for Preact X
I finally (FINALLY) found a solution for #1293, #1308, etc etc. This doesn't use DOM hacks or anything - it just crawls up the tree of VNodes and performs a Depth-First-Search backwards from the position of AsyncComponent in order to find the most recently hydrated DOM element. Essentially, the first populated `vnode._dom` reference it finds is guaranteed to be the previous sibling of the parent element AsyncComponent is about to get rendered into. As long as AsyncComponent isn't being used load a component that renders a Fragment, the nextSibling of that discovered element is the exact node Preact will try to hydrate. In the case of an AsyncComponent wrapping a Fragment, only the first Fragment child will be hydrated. This means the remaining children will have the flicker effect, but that's pretty reasonable: ```js // app.js import Foo from 'async!./foo'; export default () => <div><Foo /></div> // foo.js export default () => <Fragment> <h1>Hello</h1> <p>world!</p> // will be culled+reinserted during hydration </Fragment> ```
1 parent ec9d82d commit 3eccf5b

File tree

1 file changed

+22
-1
lines changed

1 file changed

+22
-1
lines changed

packages/async-loader/async.js

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,28 @@ import { h, Component } from 'preact';
22

33
const PENDING = {};
44

5+
// Given a VNode, finds its previous element sibling.
6+
function getPreviousSibling(vnode) {
7+
const parent = vnode.__;
8+
if (!parent) return;
9+
let children = parent.__k;
10+
if (children) {
11+
if (!Array.isArray(children)) children = [children];
12+
// only search previous children
13+
let end = children.indexOf(vnode);
14+
if (end === -1) end = children.length;
15+
for (let i=end; i--; ) {
16+
const child = children[i];
17+
const dom = child && child.__e || getPreviousSibling(child);
18+
if (dom) return dom;
19+
}
20+
}
21+
return getPreviousSibling(parent);
22+
}
23+
524
export default function async(load) {
625
let component;
26+
727
function AsyncComponent() {
828
Component.call(this);
929

@@ -23,7 +43,8 @@ export default function async(load) {
2343
return h(component, props);
2444
}
2545

26-
const me = (this.__P || this._parentDom).lastChild;
46+
const prev = getPreviousSibling(this.__v);
47+
const me = prev && prev.nextSibling || (this.__P || this._parentDom).firstChild;
2748

2849
return (
2950
me &&

0 commit comments

Comments
 (0)