Skip to content

Commit 5c0daec

Browse files
committed
Fix hydration-suspense crash due to sCU bailout
Assisted-By: devx/f79ddc2d-9f3b-44d1-85dc-4cb2c75c5ad5
1 parent 65b32cb commit 5c0daec

2 files changed

Lines changed: 52 additions & 10 deletions

File tree

compat/test/browser/suspense-hydration.test.jsx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,49 @@ describe('suspense hydration', () => {
348348
});
349349
});
350350

351+
it('does not crash when a hydrated suspended component bails out with shouldComponentUpdate', () => {
352+
scratch.innerHTML = '<div>ssr</div>';
353+
clearLog();
354+
355+
const promise = new Promise(() => {});
356+
let update;
357+
358+
class Suspender extends React.Component {
359+
shouldComponentUpdate() {
360+
return false;
361+
}
362+
363+
render() {
364+
throw promise;
365+
}
366+
}
367+
368+
class App extends React.Component {
369+
constructor(props) {
370+
super(props);
371+
this.state = { tick: 0 };
372+
update = () => this.setState({ tick: this.state.tick + 1 });
373+
}
374+
375+
render() {
376+
return (
377+
<Suspense fallback={<div>loading</div>}>
378+
<Suspender tick={this.state.tick} />
379+
</Suspense>
380+
);
381+
}
382+
}
383+
384+
hydrate(<App />, scratch);
385+
expect(scratch.innerHTML).to.equal('<div>ssr</div>');
386+
387+
update();
388+
expect(() => rerender()).not.to.throw();
389+
expect(scratch.innerHTML).to.equal('<div>ssr</div>');
390+
expect(getLog()).to.deep.equal([]);
391+
clearLog();
392+
});
393+
351394
it('should leave DOM untouched when suspending while hydrating', () => {
352395
scratch.innerHTML = '<!-- test --><div>Hello</div>';
353396
clearLog();

src/diff/index.js

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -303,21 +303,20 @@ export function diff(
303303
excessDomChildren[excessDomChildren.indexOf(oldDom)] = NULL;
304304
}
305305
newVNode._dom = oldDom;
306-
} else {
307-
if (excessDomChildren != NULL) {
308-
for (let i = excessDomChildren.length; i--; ) {
309-
removeNode(excessDomChildren[i]);
310-
}
306+
} else if (excessDomChildren != NULL) {
307+
for (let i = excessDomChildren.length; i--; ) {
308+
removeNode(excessDomChildren[i]);
311309
}
312-
markAsForce(newVNode);
313310
}
314311
} else {
315312
newVNode._dom = oldVNode._dom;
316-
if (!newVNode._children && oldVNode._children) {
317-
newVNode._children = oldVNode._children;
318-
}
319-
if (!e.then) markAsForce(newVNode);
320313
}
314+
315+
if (newVNode._children == NULL) {
316+
newVNode._children = oldVNode._children || [];
317+
}
318+
319+
if (!e.then) markAsForce(newVNode);
321320
options._catchError(e, newVNode, oldVNode);
322321
}
323322
} else if (

0 commit comments

Comments
 (0)