Skip to content

Commit 38080ea

Browse files
committed
Handle errors during hydration (#2539)
1 parent 6590e26 commit 38080ea

File tree

2 files changed

+49
-9
lines changed

2 files changed

+49
-9
lines changed

src/diff/index.js

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -274,16 +274,22 @@ export function diff(
274274
newVNode._original = null;
275275
// if hydrating or creating initial tree, bailout preserves DOM:
276276
if (isHydrating || excessDomChildren != null) {
277-
newVNode._flags |= isHydrating
278-
? MODE_HYDRATE | MODE_SUSPENDED
279-
: MODE_SUSPENDED;
277+
if (e.then) {
278+
newVNode._flags |= isHydrating
279+
? MODE_HYDRATE | MODE_SUSPENDED
280+
: MODE_SUSPENDED;
280281

281-
while (oldDom && oldDom.nodeType === 8 && oldDom.nextSibling) {
282-
oldDom = oldDom.nextSibling;
283-
}
282+
while (oldDom && oldDom.nodeType === 8 && oldDom.nextSibling) {
283+
oldDom = oldDom.nextSibling;
284+
}
284285

285-
excessDomChildren[excessDomChildren.indexOf(oldDom)] = null;
286-
newVNode._dom = oldDom;
286+
excessDomChildren[excessDomChildren.indexOf(oldDom)] = null;
287+
newVNode._dom = oldDom;
288+
} else {
289+
for (let i = excessDomChildren.length; i--; ) {
290+
removeNode(excessDomChildren[i]);
291+
}
292+
}
287293
} else {
288294
newVNode._dom = oldVNode._dom;
289295
newVNode._children = oldVNode._children;

test/browser/hydrate.test.js

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { createElement, hydrate, Fragment } from 'preact';
1+
import { createElement, hydrate, Fragment, Component } from 'preact';
2+
import { setupRerender } from 'preact/test-utils';
23
import {
34
setupScratch,
45
teardown,
@@ -28,6 +29,7 @@ describe('hydrate()', () => {
2829
let resetRemove;
2930
let resetSetAttribute;
3031
let resetRemoveAttribute;
32+
let rerender;
3133

3234
before(() => {
3335
resetAppendChild = logCall(Element.prototype, 'appendChild');
@@ -48,6 +50,7 @@ describe('hydrate()', () => {
4850
});
4951

5052
beforeEach(() => {
53+
rerender = setupRerender();
5154
scratch = setupScratch();
5255
attributesSpy = spyOnElementAttributes();
5356
clearLog();
@@ -481,4 +484,35 @@ describe('hydrate()', () => {
481484
expect(scratch.innerHTML).to.equal('<p>hello foo</p>');
482485
expect(getLog()).to.deep.equal(['Comment.remove()', 'Comment.remove()']);
483486
});
487+
488+
it('should work with error boundaries', () => {
489+
scratch.innerHTML = '<div>Hello, World!</div>';
490+
class Root extends Component {
491+
constructor() {
492+
super();
493+
this.state = {};
494+
}
495+
496+
componentDidCatch(error) {
497+
this.setState({ error });
498+
}
499+
500+
render() {
501+
if (!this.state.error) {
502+
return <App />;
503+
} else {
504+
return <div>Error!</div>;
505+
}
506+
}
507+
}
508+
509+
function App() {
510+
throw Error();
511+
return createElement('div', {}, 'Hello, World!');
512+
}
513+
514+
hydrate(<Root />, scratch);
515+
rerender();
516+
expect(scratch.innerHTML).to.equal('<div>Error!</div>');
517+
});
484518
});

0 commit comments

Comments
 (0)