Skip to content

Commit 7debb96

Browse files
developitreznord
andauthored
Fix async-loader for Preact X (#1309)
* 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> ``` * Stop searching if we hit an element parent Co-authored-by: Anup <[email protected]>
1 parent 9f3282b commit 7debb96

File tree

1 file changed

+24
-1
lines changed

1 file changed

+24
-1
lines changed

packages/async-loader/async.js

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

33
const PENDING = {};
44

5+
// Given a VNode, finds its previous element sibling
6+
function getPreviousSibling(vnode, inner) {
7+
// in an element parent with no preceeding siblings means we're the first child
8+
if (typeof vnode.type === 'string') return null;
9+
const parent = vnode.__;
10+
if (!parent) return;
11+
let children = parent.__k;
12+
if (children) {
13+
if (!Array.isArray(children)) children = [children];
14+
// only search previous children
15+
let end = children.indexOf(vnode);
16+
if (end === -1) end = children.length;
17+
for (let i=end; i--; ) {
18+
const child = children[i];
19+
const dom = child && child.__e || getPreviousSibling(child, true);
20+
if (dom) return dom;
21+
}
22+
}
23+
if (!inner) return getPreviousSibling(parent);
24+
}
25+
526
export default function async(load) {
627
let component;
28+
729
function AsyncComponent() {
830
Component.call(this);
931

@@ -23,7 +45,8 @@ export default function async(load) {
2345
return h(component, props);
2446
}
2547

26-
const me = (this.__P || this._parentDom).lastChild;
48+
const prev = getPreviousSibling(this.__v);
49+
const me = prev && prev.nextSibling || (this.__P || this._parentDom).firstChild;
2750

2851
return (
2952
me &&

0 commit comments

Comments
 (0)