Skip to content

Commit 7d80f64

Browse files
committed
Merge pull request #103 from jslatts/clientOnly
Add HtmlHelperExtension flag to turn off server side rendering
2 parents 90cbc7e + d459883 commit 7d80f64

File tree

5 files changed

+63
-12
lines changed

5 files changed

+63
-12
lines changed

src/React.AspNet/HtmlHelperExtensions.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,21 +66,23 @@ private static IReactEnvironment Environment
6666
/// <param name="props">Props to initialise the component with</param>
6767
/// <param name="htmlTag">HTML tag to wrap the component in. Defaults to &lt;div&gt;</param>
6868
/// <param name="containerId">ID to use for the container HTML tag. Defaults to an auto-generated ID</param>
69+
/// <param name="clientOnly">Skip rendering server-side and only output client-side initialisation code. Defaults to <c>false</c></param>
6970
/// <returns>The component's HTML</returns>
7071
public static IHtmlString React<T>(
7172
this IHtmlHelper htmlHelper,
7273
string componentName,
7374
T props,
7475
string htmlTag = null,
75-
string containerId = null
76+
string containerId = null,
77+
bool clientOnly = false
7678
)
7779
{
7880
var reactComponent = Environment.CreateComponent(componentName, props, containerId);
7981
if (!string.IsNullOrEmpty(htmlTag))
8082
{
8183
reactComponent.ContainerTag = htmlTag;
8284
}
83-
var result = reactComponent.RenderHtml();
85+
var result = reactComponent.RenderHtml(clientOnly);
8486
return new HtmlString(result);
8587
}
8688

@@ -95,21 +97,23 @@ public static IHtmlString React<T>(
9597
/// <param name="props">Props to initialise the component with</param>
9698
/// <param name="htmlTag">HTML tag to wrap the component in. Defaults to &lt;div&gt;</param>
9799
/// <param name="containerId">ID to use for the container HTML tag. Defaults to an auto-generated ID</param>
100+
/// <param name="clientOnly">Skip rendering server-side and only output client-side initialisation code. Defaults to <c>false</c></param>
98101
/// <returns>The component's HTML</returns>
99102
public static IHtmlString ReactWithInit<T>(
100103
this IHtmlHelper htmlHelper,
101104
string componentName,
102105
T props,
103106
string htmlTag = null,
104-
string containerId = null
107+
string containerId = null,
108+
bool clientOnly = false
105109
)
106110
{
107111
var reactComponent = Environment.CreateComponent(componentName, props, containerId);
108112
if (!string.IsNullOrEmpty(htmlTag))
109113
{
110114
reactComponent.ContainerTag = htmlTag;
111115
}
112-
var html = reactComponent.RenderHtml();
116+
var html = reactComponent.RenderHtml(clientOnly);
113117
var script = new TagBuilder("script")
114118
{
115119
InnerHtml = reactComponent.RenderJavaScript()
@@ -132,4 +136,4 @@ public static IHtmlString ReactInitJavaScript(this IHtmlHelper htmlHelper)
132136
return new HtmlString(tag.ToString());
133137
}
134138
}
135-
}
139+
}

src/React.Core/IReactComponent.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,9 @@ public interface IReactComponent
3838
/// Renders the HTML for this component. This will execute the component server-side and
3939
/// return the rendered HTML.
4040
/// </summary>
41+
/// <param name="renderContainerOnly">Only renders component container. Used for client-side only rendering.</param>
4142
/// <returns>HTML</returns>
42-
string RenderHtml();
43+
string RenderHtml(bool renderContainerOnly = false);
4344

4445
/// <summary>
4546
/// Renders the JavaScript required to initialise this component client-side. This will
@@ -49,4 +50,4 @@ public interface IReactComponent
4950
/// <returns>JavaScript</returns>
5051
string RenderJavaScript();
5152
}
52-
}
53+
}

src/React.Core/ReactComponent.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,15 +78,20 @@ public ReactComponent(IReactEnvironment environment, IReactSiteConfiguration con
7878
/// Renders the HTML for this component. This will execute the component server-side and
7979
/// return the rendered HTML.
8080
/// </summary>
81+
/// <param name="renderContainerOnly">Only renders component container. Used for client-side only rendering.</param>
8182
/// <returns>HTML</returns>
82-
public virtual string RenderHtml()
83+
public virtual string RenderHtml(bool renderContainerOnly = false)
8384
{
8485
EnsureComponentExists();
8586
try
86-
{
87-
var html = _environment.Execute<string>(
88-
string.Format("React.renderToString({0})", GetComponentInitialiser())
87+
{
88+
var html = string.Empty;
89+
if (!renderContainerOnly)
90+
{
91+
html = _environment.Execute<string>(
92+
string.Format("React.renderToString({0})", GetComponentInitialiser())
8993
);
94+
}
9095
return string.Format(
9196
"<{2} id=\"{0}\">{1}</{2}>",
9297
ContainerId,

src/React.Tests/Core/ReactComponentTest.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,25 @@ public void RenderHtmlShouldWrapComponentInDiv()
6363
Assert.AreEqual(@"<div id=""container"">[HTML]</div>", result);
6464
}
6565

66+
[Test]
67+
public void RenderHtmlShouldNotRenderComponentHTML()
68+
{
69+
var environment = new Mock<IReactEnvironment>();
70+
environment.Setup(x => x.Execute<bool>("typeof Foo !== 'undefined'")).Returns(true);
71+
environment.Setup(x => x.Execute<string>(@"React.renderToString(React.createElement(Foo, {""hello"":""World""}))"))
72+
.Returns("[HTML]");
73+
var config = new Mock<IReactSiteConfiguration>();
74+
75+
var component = new ReactComponent(environment.Object, config.Object, "Foo", "container")
76+
{
77+
Props = new { hello = "World" }
78+
};
79+
var result = component.RenderHtml(renderContainerOnly: true);
80+
81+
Assert.AreEqual(@"<div id=""container""></div>", result);
82+
environment.Verify(x => x.Execute(It.IsAny<string>()), Times.Never);
83+
}
84+
6685
[Test]
6786
public void RenderHtmlShouldWrapComponentInCustomElement()
6887
{

src/React.Tests/Mvc/HtmlHelperExtensionsTests.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ private Mock<IReactEnvironment> ConfigureMockEnvironment()
3333
public void ReactWithInitShouldReturnHtmlAndScript()
3434
{
3535
var component = new Mock<IReactComponent>();
36-
component.Setup(x => x.RenderHtml()).Returns("HTML");
36+
component.Setup(x => x.RenderHtml(false)).Returns("HTML");
3737
component.Setup(x => x.RenderJavaScript()).Returns("JS");
3838
var environment = ConfigureMockEnvironment();
3939
environment.Setup(x => x.CreateComponent(
@@ -54,5 +54,27 @@ public void ReactWithInitShouldReturnHtmlAndScript()
5454
);
5555

5656
}
57+
58+
[Test]
59+
public void ReactWithClientOnlyTrueShouldCallRenderHtmlWithTrue()
60+
{
61+
var component = new Mock<IReactComponent>();
62+
component.Setup(x => x.RenderHtml(true)).Returns("HTML");
63+
var environment = ConfigureMockEnvironment();
64+
environment.Setup(x => x.CreateComponent(
65+
"ComponentName",
66+
new {},
67+
null
68+
)).Returns(component.Object);
69+
70+
var result = HtmlHelperExtensions.React(
71+
htmlHelper: null,
72+
componentName: "ComponentName",
73+
props: new { },
74+
htmlTag: "span",
75+
clientOnly: true
76+
);
77+
component.Verify(x => x.RenderHtml(It.Is<bool>(y => y == true)), Times.Once);
78+
}
5779
}
5880
}

0 commit comments

Comments
 (0)