RenderToStringAsync(string componentName, object? prop
public async Task RenderAsync(TextWriter writer, string componentName, object? props = null,
RenderOptions? options = null)
{
- var component = new Component(componentName, props);
+ options ??= new RenderOptions();
+ var component = new Component(componentName, props, options.ServerOnly ? RenderingMode.Server : RenderingMode.ClientAndServer);
Components.Add(component);
await writer.WriteAsync($"").ConfigureAwait(false);
@@ -132,8 +133,14 @@ public async Task RenderAsync(TextWriter writer, string componentName, object? p
return;
}
- var result = await InvokeRenderTo
(component, props,
- options ?? new RenderOptions()).ConfigureAwait(false);
+ var streamingOptions = new
+ {
+ options.EnableStreaming,
+ options.ServerOnly,
+ IdentifierPrefix = _config.UseIdentifierPrefix ? component.ContainerId : null,
+ };
+
+ var result = await InvokeRenderTo(component, props, streamingOptions).ConfigureAwait(false);
using var reader = new StreamReader(await result.Content.ReadAsStreamAsync().ConfigureAwait(false));
@@ -172,7 +179,7 @@ public async Task> GetAvailableComponentNames()
return result!;
}
-
+
private static Stream GetStreamFromEmbeddedScript(string scriptName)
{
var currentAssembly = typeof(ReactService).Assembly;
@@ -203,7 +210,7 @@ public string GetInitJavascript()
private string GetInitJavascriptSource(Component c)
{
- var shouldHydrate = !_config.IsServerSideDisabled && c.RenderingMode.HasFlag(RenderingMode.Server);
+ var shouldHydrate = !_config.IsServerSideDisabled && c.RenderingMode == RenderingMode.ClientAndServer;
return shouldHydrate ? Hydrate(c) : Render(c);
}
@@ -220,27 +227,40 @@ private string CreateElement(Component component)
private string Render(Component component)
{
- var bootstrapScript = $"(window.{_config.NameOfObjectToSaveProps} = window.{_config.NameOfObjectToSaveProps} || {{}})[\"{component.JsonContainerId}\"] = {_jsonService.Serialize(component.Props)};";
+ var bootstrapScript =
+ $"(window.{_config.NameOfObjectToSaveProps} = window.{_config.NameOfObjectToSaveProps} || {{}})[\"{component.JsonContainerId}\"] = {_jsonService.Serialize(component.Props)};";
+
+ var elementById = GetElementById(component.ContainerId);
+ var element = CreateElement(component);
+ var options = GetIdentifierPrefix(component);
return bootstrapScript + (_config.ReactVersion.Major < 18
- ? $"ReactDOM.render({CreateElement(component)}, {GetElementById(component.ContainerId)}, {{ identifierPrefix: '{component.ContainerId}' }});"
- : $"ReactDOMClient.createRoot({GetElementById(component.ContainerId)}).render({CreateElement(component)}, {{ identifierPrefix: '{component.ContainerId}' }});");
+ ? $"ReactDOM.render({element}, {elementById});"
+ : $"ReactDOMClient.createRoot({elementById}{options}).render({element});");
}
private string Hydrate(Component component)
{
+ var elementById = GetElementById(component.ContainerId);
+ var element = CreateElement(component);
+ var options = GetIdentifierPrefix(component);
+
return _config.ReactVersion.Major < 18
- ? $"ReactDOM.hydrate({CreateElement(component)}, {GetElementById(component.ContainerId)}, {{ identifierPrefix: '{component.ContainerId}' }});"
- : $"ReactDOMClient.hydrateRoot({GetElementById(component.ContainerId)}, {CreateElement(component)}, {{ identifierPrefix: '{component.ContainerId}' }});";
+ ? $"ReactDOM.hydrate({element}, {elementById});"
+ : $"ReactDOMClient.hydrateRoot({elementById}, {element}{options});";
}
+
+ private string GetIdentifierPrefix(Component component) => _config.UseIdentifierPrefix
+ ? $", {{ identifierPrefix: '{component.ContainerId}' }}"
+ : string.Empty;
}
public class RenderOptions
{
public RenderOptions(bool serverOnly = false, bool enableStreaming = true)
{
- this.ServerOnly = serverOnly;
- this.EnableStreaming = enableStreaming;
+ ServerOnly = serverOnly;
+ EnableStreaming = enableStreaming;
}
public bool ServerOnly { get; }
diff --git a/Forte.Web.React/Scripts/renderToPipeableStream.js b/Forte.Web.React/Scripts/renderToPipeableStream.js
index da7e8d2..a67f84b 100644
--- a/Forte.Web.React/Scripts/renderToPipeableStream.js
+++ b/Forte.Web.React/Scripts/renderToPipeableStream.js
@@ -71,7 +71,8 @@ module.exports = (
onError(err) {
error = err;
console.error(err);
- },
+ },
+ identifierPrefix: options.identifierPrefix,
}
);
diff --git a/examples/Forte.Web.React.Examples.Core/Pages/Example.cshtml b/examples/Forte.Web.React.Examples.Core/Pages/Example.cshtml
index 5a0dee2..21ab5e6 100644
--- a/examples/Forte.Web.React.Examples.Core/Pages/Example.cshtml
+++ b/examples/Forte.Web.React.Examples.Core/Pages/Example.cshtml
@@ -16,7 +16,7 @@
In Client Side Rendering, the rendering process is deferred until the page is loaded in the browser. This means that the initial HTML page is lightweight and doesn't contain the component's content. Instead, the component is initialized and rendered using JavaScript on the client side. This allows for dynamic updates and interactions.
- @await Html.ReactAsync(new ExampleComponent { Props = Model.Props, RenderingMode = RenderingMode.Client })
+ @(await Html.ReactAsync(new ExampleComponent { Props = Model.Props, RenderingMode = RenderingMode.Client }))
@@ -28,7 +28,7 @@
In Server Side Rendering, the component is initially rendered on the server as static markup. This pre-rendered content is sent to the browser, providing faster initial page loads and improved SEO. However, there is no client-side hydration, meaning the component remains static without interactive features.
- @await Html.ReactAsync(new ExampleComponent { Props = Model.Props, RenderingMode = RenderingMode.Server })
+ @(await Html.ReactAsync(new ExampleComponent { Props = Model.Props, RenderingMode = RenderingMode.Server }))
@@ -40,7 +40,7 @@
In this approach, the component is first rendered as static markup on the server, similar to SSR. However, additional client-side JavaScript is used to "hydrate" the static markup, enabling interactivity and dynamic behavior. This combines the benefits of both SSR and CSR.
- @await Html.ReactAsync(new ExampleComponent { Props = Model.Props, RenderingMode = RenderingMode.ClientAndServer })
+ @(await Html.ReactAsync(new ExampleComponent { Props = Model.Props, RenderingMode = RenderingMode.ClientAndServer }))
diff --git a/examples/Forte.Web.React.Examples.Core/Pages/Example.cshtml.cs b/examples/Forte.Web.React.Examples.Core/Pages/Example.cshtml.cs
index a5b93a6..9a7a33c 100644
--- a/examples/Forte.Web.React.Examples.Core/Pages/Example.cshtml.cs
+++ b/examples/Forte.Web.React.Examples.Core/Pages/Example.cshtml.cs
@@ -12,7 +12,7 @@ public void OnGet(int initCount = 0, string? text = null)
Props = new ExampleComponentProps
{
InitCount = initCount,
- Text = text ?? "Use query parameters 'initCount' and 'text' to change values in the React component",
+ Text = string.IsNullOrEmpty(text) ? "Use query parameters 'initCount' and 'text' to change values in the React component" : text,
};
}
}
\ No newline at end of file
diff --git a/examples/Forte.Web.React.Examples.Core/Program.cs b/examples/Forte.Web.React.Examples.Core/Program.cs
index dcb67ad..922c937 100644
--- a/examples/Forte.Web.React.Examples.Core/Program.cs
+++ b/examples/Forte.Web.React.Examples.Core/Program.cs
@@ -32,6 +32,6 @@
var dir = app.Environment.WebRootPath;
var js = Directory.GetFiles(Path.Combine(dir, "Client/dist/assets")).First(f => f.EndsWith(".js"));
-app.UseReact(new[] { js }, new Version(18, 2, 0), strictMode: true);
+app.UseReact(new[] { js }, new Version(18, 2, 0), strictMode: true, useCache: app.Environment.IsDevelopment());
app.Run();
\ No newline at end of file