Skip to content

Commit

Permalink
Code changes to provide a preliminary implementation of the v2 API, a…
Browse files Browse the repository at this point in the history
…s a result of issue facebook#2. Still TODO: change documentation and do some sanity perf testing to make sure that streaming isn't impacted by the code flow changes.
  • Loading branch information
aickin committed Oct 24, 2015
1 parent 5ef5b4e commit f5d3a81
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 95 deletions.
140 changes: 99 additions & 41 deletions src/renderers/dom/server/ReactServerAsyncRendering.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,31 +89,60 @@ function renderToStringStream(element, stream, options) {
'renderToStringStream(): You must pass a valid ReactElement.'
);

var usingV1 = false;
// deprecation warning for version 2. The v1 API allowed you to pass in a stream and returned
// a Promise of a hash ; the v2 API returns a stream with a .hash property.
// v1 also allowed an options hash, which v2 will not.
if (stream) {
usingV1 = true;
console.error(
"You are using v1.x of the renderToString API, which is deprecated. " +
"Instead of accepting a stream parameter and returning a Promise of a hash, the API " +
"now returns a stream with a hash Promise property. " +
"Support for this version of the API will be removed in the 3.0.0 version of react-dom-stream. " +
"Please update your code, and for more info, check out (TODO: add URL here)."
);
} else {
stream = require("stream").PassThrough();
}

var bufferSize = 10000;
if (options && options.bufferSize) {
console.error(
"The options hash and bufferSize arguments have been deprecated and will be removed in " +
"the v3.0.0 of react-dom-stream. " +
"Please update your code, and for more info, check out (TODO: add URL here)."
);
bufferSize = options.bufferSize;
}
var transaction;
try {
ReactUpdates.injection.injectBatchingStrategy(ReactServerBatchingStrategy);

var id = ReactInstanceHandles.createReactRootID();
transaction = ReactServerRenderingTransaction.getPooled(false);

stream = bufferedStream(stream, bufferSize);
stream = hashedStream(stream);
var hash = transaction.perform(function() {
var componentInstance = instantiateReactComponent(element, null);
componentInstance.mountComponentAsync(id, transaction, emptyObject, stream);
stream.flush();
return stream.hash();
}, null);
return Promise.resolve(hash);
} finally {
ReactServerRenderingTransaction.release(transaction);
// Revert to the DOM batching strategy since these two renderers
// currently share these stateful modules.
ReactUpdates.injection.injectBatchingStrategy(ReactDefaultBatchingStrategy);
var hashPromise = new Promise(function(resolve, reject) {
var transaction;
try {
ReactUpdates.injection.injectBatchingStrategy(ReactServerBatchingStrategy);

var id = ReactInstanceHandles.createReactRootID();
transaction = ReactServerRenderingTransaction.getPooled(false);

var wrappedStream = hashedStream(bufferedStream(stream, bufferSize));
transaction.perform(function() {
var componentInstance = instantiateReactComponent(element, null);
componentInstance.mountComponentAsync(id, transaction, emptyObject, wrappedStream);
wrappedStream.flush();
resolve(wrappedStream.hash());
}, null);
} finally {
ReactServerRenderingTransaction.release(transaction);
// Revert to the DOM batching strategy since these two renderers
// currently share these stateful modules.
ReactUpdates.injection.injectBatchingStrategy(ReactDefaultBatchingStrategy);
}
});

if (usingV1) {
return hashPromise;
} else {
stream.hash = hashPromise;
return stream;
}
}

Expand All @@ -129,30 +158,59 @@ function renderToStaticMarkupStream(element, stream, options) {
'renderToStaticMarkupStream(): You must pass a valid ReactElement.'
);

var usingV1 = false;
// deprecation warning for version 2. The v1 API allowed you to pass in a stream and returned
// a Promise of a hash ; the v2 API returns a stream with a .hash property.
// v1 also allowed an options hash, which v2 will not.
if (stream) {
usingV1 = true;
console.error(
"You are using v1.x of the renderToMarkupStream API, which is deprecated. " +
"Instead of accepting a stream parameter and returning a Promise, the API now just returns a stream. " +
"Support for this version of the API will be removed in the 3.0.0 version of react-dom-stream. " +
"Please update your code, and for more info, check out (TODO: add URL here)."
);
} else {
stream = require("stream").PassThrough();
}

var bufferSize = 10000;
if (options && options.bufferSize) {
console.error(
"The options hash and bufferSize arguments have been deprecated and will be removed in " +
"the v3.0.0 of react-dom-stream. " +
"Please update your code, and for more info, check out (TODO: add URL here)."
);
bufferSize = options.bufferSize;
}
var transaction;
try {
ReactUpdates.injection.injectBatchingStrategy(ReactServerBatchingStrategy);

var id = ReactInstanceHandles.createReactRootID();
transaction = ReactServerRenderingTransaction.getPooled(true);

stream = bufferedStream(stream, bufferSize);
transaction.perform(function() {
var componentInstance = instantiateReactComponent(element, null);
componentInstance.mountComponentAsync(id, transaction, emptyObject, stream);
stream.flush();
}, null);

return Promise.resolve(null);
} finally {
ReactServerRenderingTransaction.release(transaction);
// Revert to the DOM batching strategy since these two renderers
// currently share these stateful modules.
ReactUpdates.injection.injectBatchingStrategy(ReactDefaultBatchingStrategy);
var promise = new Promise(function(resolve, reject) {
var transaction;
try {
ReactUpdates.injection.injectBatchingStrategy(ReactServerBatchingStrategy);

var id = ReactInstanceHandles.createReactRootID();
transaction = ReactServerRenderingTransaction.getPooled(true);

var wrappedStream = bufferedStream(stream, bufferSize);
transaction.perform(function() {
var componentInstance = instantiateReactComponent(element, null);
componentInstance.mountComponentAsync(id, transaction, emptyObject, wrappedStream);
wrappedStream.flush();
}, null);

return Promise.resolve(null);
} finally {
ReactServerRenderingTransaction.release(transaction);
// Revert to the DOM batching strategy since these two renderers
// currently share these stateful modules.
ReactUpdates.injection.injectBatchingStrategy(ReactDefaultBatchingStrategy);
}
});

if (usingV1) {
return promise;
} else {
return stream;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,8 @@ describe('ReactServerAsyncRendering', function() {
});

ReactServerAsyncRendering.renderToStringStream(
<span>hello world</span>,
stream
);

stream.end();
<span>hello world</span>
).pipe(stream);
});

it('should generate simple markup for self-closing tags', function() {
Expand All @@ -69,11 +66,8 @@ describe('ReactServerAsyncRendering', function() {
});

ReactServerAsyncRendering.renderToStringStream(
<img />,
stream
);

stream.end();
<img />
).pipe(stream);
});

it('should generate simple markup for attribute with `>` symbol', function() {
Expand All @@ -82,12 +76,9 @@ describe('ReactServerAsyncRendering', function() {
'<img data-attr="&gt;" ' + ID_ATTRIBUTE_NAME + '="[^"]+"/>'
);
});
var response = ReactServerAsyncRendering.renderToStringStream(
<img data-attr=">" />,
stream
);

stream.end();
ReactServerAsyncRendering.renderToStringStream(
<img data-attr=">" />
).pipe(stream);
});

it('should not register event listeners', function() {
Expand All @@ -96,10 +87,9 @@ describe('ReactServerAsyncRendering', function() {
var cb = mocks.getMockFunction();

ReactServerAsyncRendering.renderToStringStream(
<span onClick={cb}>hello world</span>,
stream
);
stream.end();
<span onClick={cb}>hello world</span>
).pipe(stream);

expect(EventPluginHub.__getListenerBank()).toEqual({});
});

Expand All @@ -124,11 +114,9 @@ describe('ReactServerAsyncRendering', function() {
return <span>My name is {this.props.name}</span>;
},
});
var response = ReactServerAsyncRendering.renderToStringStream(
<Parent />, stream
);

stream.end();
ReactServerAsyncRendering.renderToStringStream(
<Parent />
).pipe(stream);
});

it('should only execute certain lifecycle methods', function() {
Expand Down Expand Up @@ -174,11 +162,9 @@ describe('ReactServerAsyncRendering', function() {
},
});

var response = ReactServerAsyncRendering.renderToStringStream(
<TestComponent />, stream
);

stream.end();
ReactServerAsyncRendering.renderToStringStream(
<TestComponent />
).pipe(stream);

expect(lifecycle).toEqual(
['getInitialState', 'componentWillMount', 'render']
Expand Down Expand Up @@ -267,9 +253,13 @@ describe('ReactServerAsyncRendering', function() {
});

var hash;
ReactServerAsyncRendering.renderToStringStream(
<TestComponent name="x" />, stream
).then(function(hashValue) {
var renderedStream = ReactServerAsyncRendering.renderToStringStream(
<TestComponent name="x" />
);

renderedStream.pipe(stream, {end:false});

renderedStream.hash.then(function(hashValue) {
hash = hashValue;
stream.end();
})
Expand Down Expand Up @@ -306,11 +296,9 @@ describe('ReactServerAsyncRendering', function() {
},
});

var response = ReactServerAsyncRendering.renderToStaticMarkupStream(
<TestComponent />, stream
);

stream.end();
ReactServerAsyncRendering.renderToStaticMarkupStream(
<TestComponent />
).pipe(stream);
});

it('should not put checksum and React ID on text components', function() {
Expand All @@ -323,21 +311,19 @@ describe('ReactServerAsyncRendering', function() {
},
});

var response = ReactServerAsyncRendering.renderToStaticMarkupStream(
<TestComponent />, stream
);

stream.end();
ReactServerAsyncRendering.renderToStaticMarkupStream(
<TestComponent />
).pipe(stream);
});

it('should not register event listeners', function() {
var EventPluginHub = require('EventPluginHub');
var cb = mocks.getMockFunction();

ReactServerAsyncRendering.renderToStringStream(
<span onClick={cb}>hello world</span>,
concatStream({encoding: "string"}, function(result) {})
);
<span onClick={cb}>hello world</span>)
.pipe(concatStream({encoding: "string"}, function(result) {}));

expect(EventPluginHub.__getListenerBank()).toEqual({});
});

Expand Down Expand Up @@ -380,11 +366,10 @@ describe('ReactServerAsyncRendering', function() {
expect(result).toBe('<span>Component name: TestComponent</span>');
});

var response = ReactServerAsyncRendering.renderToStaticMarkupStream(
<TestComponent />, stream
);
ReactServerAsyncRendering.renderToStaticMarkupStream(
<TestComponent />
).pipe(stream);

stream.end();
expect(lifecycle).toEqual(
['getInitialState', 'componentWillMount', 'render']
);
Expand Down Expand Up @@ -429,10 +414,8 @@ describe('ReactServerAsyncRendering', function() {
throw new Error('Browser reconcile transaction should not be used');
};
ReactServerAsyncRendering.renderToStringStream(
<Component />, stream
);

stream.end();
<Component />
).pipe(stream);
});
});
});

0 comments on commit f5d3a81

Please sign in to comment.