diff --git a/src/React.AspNet/HtmlHelperExtensions.cs b/src/React.AspNet/HtmlHelperExtensions.cs
index b136ca42f..514c38840 100644
--- a/src/React.AspNet/HtmlHelperExtensions.cs
+++ b/src/React.AspNet/HtmlHelperExtensions.cs
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright (c) 2014-Present, Facebook, Inc.
* All rights reserved.
*
@@ -54,7 +54,7 @@ private static IReactEnvironment Environment
/// HTML tag to wrap the component in. Defaults to <div>
/// ID to use for the container HTML tag. Defaults to an auto-generated ID
/// Skip rendering server-side and only output client-side initialisation code. Defaults to false
- /// Skip rendering React specific data-attributes during server side rendering. Defaults to false
+ /// Skip rendering React specific data-attributes, container and client-side initialisation during server side rendering. Defaults to false
/// HTML class(es) to set on the container tag
/// A custom exception handler that will be called if a component throws during a render. Args: (Exception ex, string componentName, string containerId)
/// The component's HTML
@@ -72,7 +72,7 @@ public static IHtmlString React(
{
try
{
- var reactComponent = Environment.CreateComponent(componentName, props, containerId, clientOnly);
+ var reactComponent = Environment.CreateComponent(componentName, props, containerId, clientOnly, serverOnly);
if (!string.IsNullOrEmpty(htmlTag))
{
reactComponent.ContainerTag = htmlTag;
diff --git a/src/React.Core/IReactComponent.cs b/src/React.Core/IReactComponent.cs
index 3cdf7f794..b4a6d4428 100644
--- a/src/React.Core/IReactComponent.cs
+++ b/src/React.Core/IReactComponent.cs
@@ -41,6 +41,11 @@ public interface IReactComponent
///
string ContainerClass { get; set; }
+ ///
+ /// Get or sets if this components only should be rendered server side
+ ///
+ bool ServerOnly { get; set; }
+
///
/// Renders the HTML for this component. This will execute the component server-side and
/// return the rendered HTML.
diff --git a/src/React.Core/IReactEnvironment.cs b/src/React.Core/IReactEnvironment.cs
index 39fccd721..56cbbf5a1 100644
--- a/src/React.Core/IReactEnvironment.cs
+++ b/src/React.Core/IReactEnvironment.cs
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright (c) 2014-Present, Facebook, Inc.
* All rights reserved.
*
@@ -81,8 +81,9 @@ public interface IReactEnvironment
/// Props to use
/// ID to use for the container HTML tag. Defaults to an auto-generated ID
/// True if server-side rendering will be bypassed. Defaults to false.
+ /// True if this component only should be rendered server-side. Defaults to false.
/// The component
- IReactComponent CreateComponent(string componentName, T props, string containerId = null, bool clientOnly = false);
+ IReactComponent CreateComponent(string componentName, T props, string containerId = null, bool clientOnly = false, bool serverOnly = false);
///
/// Adds the provided to the list of components to render client side.
diff --git a/src/React.Core/ReactComponent.cs b/src/React.Core/ReactComponent.cs
index e736604f0..012100403 100644
--- a/src/React.Core/ReactComponent.cs
+++ b/src/React.Core/ReactComponent.cs
@@ -68,6 +68,11 @@ public class ReactComponent : IReactComponent
///
public string ContainerClass { get; set; }
+ ///
+ /// Get or sets if this components only should be rendered server side
+ ///
+ public bool ServerOnly { get; set; }
+
///
/// Gets or sets the props for this component
///
@@ -130,6 +135,11 @@ public virtual string RenderHtml(bool renderContainerOnly = false, bool renderSe
? string.Format("ReactDOMServer.renderToStaticMarkup({0})", GetComponentInitialiser())
: string.Format("ReactDOMServer.renderToString({0})", GetComponentInitialiser());
html = _environment.Execute(reactRenderCommand);
+
+ if (renderServerOnly)
+ {
+ return html;
+ }
}
catch (JsRuntimeException ex)
{
diff --git a/src/React.Core/ReactEnvironment.cs b/src/React.Core/ReactEnvironment.cs
index e3a706812..6b1101762 100644
--- a/src/React.Core/ReactEnvironment.cs
+++ b/src/React.Core/ReactEnvironment.cs
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright (c) 2014-Present, Facebook, Inc.
* All rights reserved.
*
@@ -287,8 +287,9 @@ public virtual bool HasVariable(string name)
/// Props to use
/// ID to use for the container HTML tag. Defaults to an auto-generated ID
/// True if server-side rendering will be bypassed. Defaults to false.
+ /// True if this component only should be rendered server-side. Defaults to false.
/// The component
- public virtual IReactComponent CreateComponent(string componentName, T props, string containerId = null, bool clientOnly = false)
+ public virtual IReactComponent CreateComponent(string componentName, T props, string containerId = null, bool clientOnly = false, bool serverOnly = false)
{
if (!clientOnly)
{
@@ -297,7 +298,8 @@ public virtual IReactComponent CreateComponent(string componentName, T props,
var component = new ReactComponent(this, _config, componentName, containerId)
{
- Props = props
+ Props = props,
+ ServerOnly = serverOnly
};
_components.Add(component);
return component;
@@ -339,8 +341,11 @@ public virtual string GetInitJavaScript(bool clientOnly = false)
foreach (var component in _components)
{
- fullScript.Append(component.RenderJavaScript());
- fullScript.AppendLine(";");
+ if(!component.ServerOnly)
+ {
+ fullScript.Append(component.RenderJavaScript());
+ fullScript.AppendLine(";");
+ }
}
return fullScript.ToString();
diff --git a/tests/React.Tests/Core/ReactComponentTest.cs b/tests/React.Tests/Core/ReactComponentTest.cs
index 234825b05..66fe070da 100644
--- a/tests/React.Tests/Core/ReactComponentTest.cs
+++ b/tests/React.Tests/Core/ReactComponentTest.cs
@@ -124,6 +124,45 @@ public void RenderHtmlShouldWrapComponentInCustomElement()
Assert.Equal(@"[HTML]", result);
}
+ [Fact]
+ public void RenderHtmlShouldNotRenderComponentWhenContainerOnly()
+ {
+ var config = new Mock();
+ config.Setup(x => x.UseServerSideRendering).Returns(true);
+ var environment = new Mock();
+ environment.Setup(x => x.Execute("typeof Foo !== 'undefined'")).Returns(true);
+ environment.Setup(x => x.Execute(@"ReactDOMServer.renderToString(React.createElement(Foo, {""hello"":""World""}))"))
+ .Returns("[HTML]");
+
+ var component = new ReactComponent(environment.Object, config.Object, "Foo", "container")
+ {
+ Props = new { hello = "World" },
+ ContainerTag = "span"
+ };
+ var result = component.RenderHtml(true, false);
+
+ Assert.Equal(@"", result);
+ }
+
+ [Fact]
+ public void RenderHtmlShouldNotWrapComponentWhenServerSideOnly()
+ {
+ var config = new Mock();
+ config.Setup(x => x.UseServerSideRendering).Returns(true);
+ var environment = new Mock();
+ environment.Setup(x => x.Execute("typeof Foo !== 'undefined'")).Returns(true);
+ environment.Setup(x => x.Execute(@"ReactDOMServer.renderToStaticMarkup(React.createElement(Foo, {""hello"":""World""}))"))
+ .Returns("[HTML]");
+
+ var component = new ReactComponent(environment.Object, config.Object, "Foo", "container")
+ {
+ Props = new { hello = "World" },
+ };
+ var result = component.RenderHtml(false, true);
+
+ Assert.Equal(@"[HTML]", result);
+ }
+
[Fact]
public void RenderHtmlShouldAddClassToElement()
{
diff --git a/tests/React.Tests/Core/ReactEnvironmentTest.cs b/tests/React.Tests/Core/ReactEnvironmentTest.cs
index 4017051ba..9c6a4914e 100644
--- a/tests/React.Tests/Core/ReactEnvironmentTest.cs
+++ b/tests/React.Tests/Core/ReactEnvironmentTest.cs
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright (c) 2014-Present, Facebook, Inc.
* All rights reserved.
*
@@ -137,6 +137,17 @@ public void CreatesIReactComponent()
Assert.Equal(";\r\n", environment.GetInitJavaScript());
}
+ [Fact]
+ public void ServerSideOnlyComponentRendersNoJavaScript()
+ {
+ var mocks = new Mocks();
+ var environment = mocks.CreateReactEnvironment();
+
+ environment.CreateComponent("HelloWorld", new { name = "Daniel" }, serverOnly: true);
+
+ Assert.Equal(string.Empty, environment.GetInitJavaScript());
+ }
+
public class Mocks
{
public Mock Engine { get; private set; }
diff --git a/tests/React.Tests/Mvc/HtmlHelperExtensionsTests.cs b/tests/React.Tests/Mvc/HtmlHelperExtensionsTests.cs
index 14d1253a4..635c8ca6a 100644
--- a/tests/React.Tests/Mvc/HtmlHelperExtensionsTests.cs
+++ b/tests/React.Tests/Mvc/HtmlHelperExtensionsTests.cs
@@ -8,13 +8,13 @@
*/
using Moq;
-using React.Web.Mvc;
using Xunit;
+using React.Web.Mvc;
namespace React.Tests.Mvc
{
public class HtmlHelperExtensionsTests
- {
+ {
///
/// Creates a mock and registers it with the IoC container
/// This is only required because can not be
@@ -36,8 +36,9 @@ public void ReactWithInitShouldReturnHtmlAndScript()
var environment = ConfigureMockEnvironment();
environment.Setup(x => x.CreateComponent(
"ComponentName",
- new {},
+ new { },
null,
+ false,
false
)).Returns(component.Object);
@@ -63,7 +64,8 @@ public void EngineIsReturnedToPoolAfterRender()
"ComponentName",
new { },
null,
- true
+ true,
+ false
)).Returns(component.Object);
environment.Verify(x => x.ReturnEngineToPool(), Times.Never);
@@ -73,9 +75,9 @@ public void EngineIsReturnedToPoolAfterRender()
props: new { },
htmlTag: "span",
clientOnly: true,
- serverOnly: true
+ serverOnly: false
);
- component.Verify(x => x.RenderHtml(It.Is(y => y == true), It.Is(z => z == true), null), Times.Once);
+ component.Verify(x => x.RenderHtml(It.Is(y => y == true), It.Is(z => z == false), null), Times.Once);
environment.Verify(x => x.ReturnEngineToPool(), Times.Once);
}
@@ -87,9 +89,10 @@ public void ReactWithClientOnlyTrueShouldCallRenderHtmlWithTrue()
var environment = ConfigureMockEnvironment();
environment.Setup(x => x.CreateComponent(
"ComponentName",
- new {},
+ new { },
null,
- true
+ true,
+ false
)).Returns(component.Object);
var result = HtmlHelperExtensions.React(
@@ -98,20 +101,22 @@ public void ReactWithClientOnlyTrueShouldCallRenderHtmlWithTrue()
props: new { },
htmlTag: "span",
clientOnly: true,
- serverOnly: true
+ serverOnly: false
);
- component.Verify(x => x.RenderHtml(It.Is(y => y == true), It.Is(z => z == true), null), Times.Once);
+ component.Verify(x => x.RenderHtml(It.Is(y => y == true), It.Is(z => z == false), null), Times.Once);
}
[Fact]
- public void ReactWithServerOnlyTrueShouldCallRenderHtmlWithTrue() {
+ public void ReactWithServerOnlyTrueShouldCallRenderHtmlWithTrue()
+ {
var component = new Mock();
- component.Setup(x => x.RenderHtml(true, true, null)).Returns("HTML");
+ component.Setup(x => x.RenderHtml(false, true, null)).Returns("HTML");
var environment = ConfigureMockEnvironment();
environment.Setup(x => x.CreateComponent(
"ComponentName",
new { },
null,
+ false,
true
)).Returns(component.Object);
@@ -120,10 +125,10 @@ public void ReactWithServerOnlyTrueShouldCallRenderHtmlWithTrue() {
componentName: "ComponentName",
props: new { },
htmlTag: "span",
- clientOnly: true,
+ clientOnly: false,
serverOnly: true
);
- component.Verify(x => x.RenderHtml(It.Is(y => y == true), It.Is(z => z == true), null), Times.Once);
+ component.Verify(x => x.RenderHtml(It.Is(y => y == false), It.Is(z => z == true), null), Times.Once);
}
}
-}
+}