From f68f002395700860fe6147e2b9b21f777802cfd3 Mon Sep 17 00:00:00 2001 From: Bo Borgerson Date: Wed, 11 Oct 2017 13:43:34 -0700 Subject: [PATCH] Client transitions still need to render in order During client transitions the root structure is built up using a state machine that requires an in-order render. First render can still go out of order because the server has already laid out the root structure. This fixes #961. --- .../react-server/core/ClientController.js | 49 +++++++++++-------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/packages/react-server/core/ClientController.js b/packages/react-server/core/ClientController.js index 98f3b74c0..ecdefe00e 100644 --- a/packages/react-server/core/ClientController.js +++ b/packages/react-server/core/ClientController.js @@ -601,26 +601,35 @@ class ClientController extends EventEmitter { } }; - // As elements become ready, prime them to render as soon as - // their mount point is available. - // - Q.all(elementPromisesOr.map((promise, index) => promise.then( - element => rootNodePromises[index] - .then(root => renderElement(element, root, index)) - .catch(e => { - // The only case where this should evaluate to false is - // when `element` is a containerClose/containerOpen object - const componentType = typeof element.type === 'function' - ? element.props.children.type.name - : 'element'; - logger.error(`Error with element ${componentType}'s lifecycle methods at index ${index}`, e); - }) - ).catch(e => logger.error(`Error with element promise ${index}`, e)) - )).then(retval.resolve); - - // Look out for a failsafe timeout from the server on our - // first render. - if (!this._previouslyRendered){ + const renderOne = (promise, index) => promise.then( + element => rootNodePromises[index] + .then(root => renderElement(element, root, index)) + .catch(e => { + // The only case where this should evaluate to false is + // when `element` is a containerClose/containerOpen object + const componentType = typeof element.type === 'function' + ? element.props.children.type.name + : 'element'; + logger.error(`Error with element ${componentType}'s lifecycle methods at index ${index}`, e); + }) + ).catch(e => logger.error(`Error with element promise ${index}`, e)) + + if (this._previouslyRendered){ + + // On client transitions the root structure is laid out using a + // state machine that requires us to render in order. + elementPromisesOr.reduce( + (chain, promise, index) => chain.then(() => renderOne(promise, index)), + Q() + ).then(retval.resolve); + } else { + + // On the first render we can go out of order because the server + // has already laid out the root structure for us. + Q.all(elementPromisesOr.map(renderOne)).then(retval.resolve); + + // Look out for a failsafe timeout from the server on our + // first render. this._failDfd.promise.then(() => { elementPromises.forEach((promise, index) => { //Reject any elements that have failed to render