Skip to content

Commit

Permalink
Stop rendering within suspense boundary once fallback triggered [major]
Browse files Browse the repository at this point in the history
Closes #5.
  • Loading branch information
overlookmotel committed Jan 29, 2019
1 parent 82ff684 commit 052972d
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 10 deletions.
20 changes: 14 additions & 6 deletions lib/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const {renderToNodeStream} = require('react-dom/server'),
{toArray} = require('react').Children;

// Imports
const {TYPE_SUSPENSE, TYPE_PROMISE, PLACEHOLDER} = require('./constants'),
const {TYPE_SUSPENSE, TYPE_PROMISE, PLACEHOLDER, NO_SSR} = require('./constants'),
{isPromise, isSuspense, last} = require('./utils'),
{treeAddHtml} = require('./tree'),
renderCapture = require('./renderCapture');
Expand Down Expand Up @@ -179,12 +179,9 @@ class PartialRenderer extends ReactDOMServerRenderer {
}

handlePromise(element, context, domNamespace, promise) {
if (promise[NO_SSR]) this.handleNoSsrPromise(promise);

// Add node to tree
// TODO Identify here if promise is flagged for no server render,
// and throw if so, to prevent later elements executing further async
// actions (fetch data etc) the results of which will never be rendered
// as the part of the tree within the boundary will be replaced by
// fallback.
const child = this.createChild(TYPE_PROMISE, null, context, domNamespace, {element, promise});

// Record child in lazy nodes array
Expand All @@ -195,6 +192,17 @@ class PartialRenderer extends ReactDOMServerRenderer {
return PLACEHOLDER;
}

handleNoSsrPromise() {
// Prevent further rendering of all children in frames down to suspense boundary.
const boundaryFrame = this.node.frame;
const {stack} = this;
for (let i = stack.length - 1; i >= 0; i--) {
const frame = stack[i];
frame.childIndex = frame.children.length;
if (frame === boundaryFrame) break;
}
}

createChild(type, frame, context, domNamespace, props) {
// Get current contexts from new Context API
const {threadID} = this;
Expand Down
8 changes: 4 additions & 4 deletions src/test/lazy.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ describe('Multiple lazy components', () => {
`));
});

itRenders.skip('prevents later elements being rendered', async ({render, openTag}) => {
itRenders('prevents later elements being rendered', async ({render, openTag}) => {
const Lazy1 = spy(lazy(() => <div>Lazy inner 1</div>));
const Lazy2 = spy(lazy(() => <div>Lazy inner 2</div>, {noSsr: true}));
const Lazy3 = spy(lazy(() => <div>Lazy inner 3</div>));
Expand Down Expand Up @@ -486,7 +486,7 @@ describe('Multiple lazy components', () => {

expect(promises[0].abort).to.be.calledOnce;
expect(promises[1].abort).to.be.calledOnce;
expect(promises[2].abort).to.be.calledOnce;
expect(promises[2]).to.be.undefined;
expect(promises[3].abort).not.to.be.called;

const h = await p;
Expand Down Expand Up @@ -515,7 +515,7 @@ describe('Multiple lazy components', () => {
expect(err).to.equal(promise);
});

itRenders.skip('prevents later elements being rendered', async ({render}) => {
itRenders('prevents later elements being rendered', async ({render}) => {
const Lazy1 = spy(lazy(() => <div>Lazy inner 1</div>));
const promise = Object.assign(new Promise(resolve => resolve()), {noSsr: true});
const Lazy2 = spy(() => {throw promise;});
Expand Down Expand Up @@ -572,7 +572,7 @@ describe('Multiple lazy components', () => {

expect(promises[0].abort).to.be.calledOnce;
expect(promises[1].abort).to.be.calledOnce;
expect(promises[2].abort).to.be.calledOnce;
expect(promises[2]).to.be.undefined;

await expect(p).to.be.rejected;
const {err} = await getPromiseState(p);
Expand Down

0 comments on commit 052972d

Please sign in to comment.