Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added global context to render in renderToString.js script. #37

Merged
merged 2 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Forte.Web.React/Configuration/ReactConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ public class ReactConfiguration
/// </summary>
public string NameOfObjectToSaveProps { get; set; } = "__reactProps";

/// <summary>
/// Name of the object used to save global data object. Default value is "__globalData".
/// <remarks>NameOfGlobalDataToSave is supported by <see cref="IReactService.RenderToStringAsync"/> method.</remarks>
/// </summary>
public string NameOfGlobalDataToSave { get; set; } = "__globalData";

/// <summary>
/// Indicates whether caching is used. Default value is "true".
/// <remarks>
Expand Down
4 changes: 3 additions & 1 deletion Forte.Web.React/ForteWebReactExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ public static void AddReact(this IServiceCollection services,
}

public static void UseReact(this IApplicationBuilder app, IEnumerable<string> scriptUrls, Version reactVersion,
bool disableServerSideRendering = false, string? nameOfObjectToSaveProps = null, bool? useCache = null, bool? strictMode = null)
bool disableServerSideRendering = false, string? nameOfObjectToSaveProps = null,
string? nameOfGlobalDataToSave = null, bool? useCache = null, bool? strictMode = null)
{
var config = app.ApplicationServices.GetService<ReactConfiguration>();

Expand All @@ -61,6 +62,7 @@ public static void UseReact(this IApplicationBuilder app, IEnumerable<string> sc
config.ScriptUrls = scriptUrls.ToList();
config.ReactVersion = reactVersion;
config.NameOfObjectToSaveProps = nameOfObjectToSaveProps ?? config.NameOfObjectToSaveProps;
config.NameOfGlobalDataToSave = nameOfGlobalDataToSave ?? config.NameOfGlobalDataToSave;
config.UseCache = useCache ?? true;
config.StrictMode = strictMode ?? false;
}
Expand Down
52 changes: 35 additions & 17 deletions Forte.Web.React/HtmlHelperExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,34 @@ namespace Forte.Web.React;
public static class HtmlHelperExtensions
{
#if NET48
public static IHtmlString React<T>(this HtmlHelper _, string componentName, T props)
public static IHtmlString React<T>(this HtmlHelper _, string componentName, T props, object? globalData = null)
{
var reactService = DependencyResolver.Current.GetService<IReactService>();
var renderedComponent = reactService.RenderToStringAsync(componentName, props).GetAwaiter().GetResult();
var renderedComponent = reactService.RenderToStringAsync(componentName, props, globalData: globalData)
.GetAwaiter().GetResult();

return new HtmlString(renderedComponent);
}

public static IHtmlString React<TComponent>(this HtmlHelper _, TComponent component) where TComponent : IReactComponent

public static IHtmlString React<TComponent>(this HtmlHelper _, TComponent component, object? globalData = null)
where TComponent : IReactComponent
{
var reactService = DependencyResolver.Current.GetService<IReactService>();
var renderedComponent = reactService.RenderToStringAsync(component.Path, null, component.RenderingMode).GetAwaiter().GetResult();
var renderedComponent = reactService
.RenderToStringAsync(component.Path, null, component.RenderingMode, globalData)
.GetAwaiter().GetResult();

return new HtmlString(renderedComponent);
}

public static IHtmlString React<TComponent, TProps>(this HtmlHelper _, TComponent component) where TComponent : IReactComponent<TProps> where TProps : IReactComponentProps

public static IHtmlString React<TComponent, TProps>(this HtmlHelper _, TComponent component,
object? globalData = null)
where TComponent : IReactComponent<TProps> where TProps : IReactComponentProps
{
var reactService = DependencyResolver.Current.GetService<IReactService>();
var renderedComponent = reactService.RenderToStringAsync(component.Path, component.Props, component.RenderingMode).GetAwaiter().GetResult();
var renderedComponent = reactService
.RenderToStringAsync(component.Path, component.Props, component.RenderingMode, globalData).GetAwaiter()
.GetResult();

return new HtmlString(renderedComponent);
}
Expand All @@ -47,28 +55,38 @@ public static IHtmlString InitJavascript(this HtmlHelper _)

return new HtmlString(reactService.GetInitJavascript());
}
}
#endif

#if NET6_0_OR_GREATER
public static async Task<IHtmlContent> ReactAsync<T>(this IHtmlHelper htmlHelper, string componentName, T props)
public static async Task<IHtmlContent> ReactAsync<T>(this IHtmlHelper htmlHelper, string componentName, T props,
object? globalData = null)
{
var reactService = htmlHelper.ViewContext.HttpContext.RequestServices.GetRequiredService<IReactService>();

return new HtmlString(await reactService.RenderToStringAsync(componentName, props));
return new HtmlString(
await reactService.RenderToStringAsync(componentName, props, globalData: globalData));
}

public static async Task<IHtmlContent> ReactAsync<TComponent>(this IHtmlHelper htmlHelper, TComponent component) where TComponent : IReactComponent

public static async Task<IHtmlContent> ReactAsync<TComponent>(this IHtmlHelper htmlHelper, TComponent component,
object? globalData = null)
where TComponent : IReactComponent
{
var reactService = htmlHelper.ViewContext.HttpContext.RequestServices.GetRequiredService<IReactService>();

return new HtmlString(await reactService.RenderToStringAsync(component.Path, null, component.RenderingMode));
return new HtmlString(
await reactService.RenderToStringAsync(component.Path, null, component.RenderingMode, globalData));
}

public static async Task<IHtmlContent> ReactAsync<TComponent, TProps>(this IHtmlHelper htmlHelper, TComponent component) where TComponent : IReactComponent<TProps> where TProps : IReactComponentProps

public static async Task<IHtmlContent> ReactAsync<TComponent, TProps>(this IHtmlHelper htmlHelper,
TComponent component, object? globalData =
null) where TComponent : IReactComponent<TProps> where TProps : IReactComponentProps
{
var reactService = htmlHelper.ViewContext.HttpContext.RequestServices.GetRequiredService<IReactService>();

return new HtmlString(await reactService.RenderToStringAsync(component.Path, component.Props, component.RenderingMode));
return new HtmlString(
await reactService.RenderToStringAsync(component.Path, component.Props, component.RenderingMode,
globalData));
}

public static IHtmlContent InitJavascript(this IHtmlHelper htmlHelper)
Expand All @@ -77,5 +95,5 @@ public static IHtmlContent InitJavascript(this IHtmlHelper htmlHelper)

return new HtmlString(reactService.GetInitJavascript());
}
#endif
}
#endif
36 changes: 23 additions & 13 deletions Forte.Web.React/React/ReactService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
Task<IReadOnlyCollection<string>> GetAvailableComponentNames();

Task RenderAsync(TextWriter writer, string componentName, object? props = null, RenderOptions? options = null);
Task<string> RenderToStringAsync(string componentName, object? props = null, RenderingMode renderingMode = RenderingMode.ClientAndServer);

Task<string> RenderToStringAsync(string componentName, object? props = null,
RenderingMode renderingMode = RenderingMode.ClientAndServer, object? globalData = null);
}

public class ReactService : IReactService
Expand Down Expand Up @@ -65,15 +67,17 @@
}
#endif

private async Task<T> InvokeRenderTo<T>(Component component, object? props = null, params object[] args)
private async Task<T> InvokeRenderTo<T>(Component component, object? props = null, object? globalData = null, params object[] args)
{
var allArgs = new List<object>()
{
component.Path,
component.JsonContainerId,
props,

Check warning on line 76 in Forte.Web.React/React/ReactService.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'item' in 'void List<object>.Add(object item)'.

Check warning on line 76 in Forte.Web.React/React/ReactService.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'item' in 'void List<object>.Add(object item)'.

Check warning on line 76 in Forte.Web.React/React/ReactService.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'item' in 'void List<object>.Add(object item)'.

Check warning on line 76 in Forte.Web.React/React/ReactService.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'item' in 'void List<object>.Add(object item)'.
_config.ScriptUrls,
_config.NameOfObjectToSaveProps,
_config.NameOfGlobalDataToSave,
globalData

Check warning on line 80 in Forte.Web.React/React/ReactService.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'item' in 'void List<object>.Add(object item)'.

Check warning on line 80 in Forte.Web.React/React/ReactService.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'item' in 'void List<object>.Add(object item)'.

Check warning on line 80 in Forte.Web.React/React/ReactService.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'item' in 'void List<object>.Add(object item)'.

Check warning on line 80 in Forte.Web.React/React/ReactService.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'item' in 'void List<object>.Add(object item)'.
};
allArgs.AddRange(args);

Expand Down Expand Up @@ -103,7 +107,8 @@
}


public async Task<string> RenderToStringAsync(string componentName, object? props = null, RenderingMode renderingMode = RenderingMode.ClientAndServer)
public async Task<string> RenderToStringAsync(string componentName, object? props = null,
RenderingMode renderingMode = RenderingMode.ClientAndServer, object? globalData = null)
{
var component = new Component(componentName, props, renderingMode);
Components.Add(component);
Expand All @@ -113,12 +118,13 @@
return WrapRenderedStringComponent(string.Empty, component);
}

var result = await InvokeRenderTo<string>(component, props).ConfigureAwait(false);
var result = await InvokeRenderTo<string>(component, props, globalData).ConfigureAwait(false);

return WrapRenderedStringComponent(result, component);
}

public async Task RenderAsync(TextWriter writer, string componentName, object? props = null, RenderOptions? options = null)
public async Task RenderAsync(TextWriter writer, string componentName, object? props = null,
RenderOptions? options = null)
{
options ??= new RenderOptions();
var component = new Component(componentName, props, options.RenderingMode);
Expand All @@ -139,7 +145,8 @@
IdentifierPrefix = _config.UseIdentifierPrefix ? component.ContainerId : null,
};

var result = await InvokeRenderTo<HttpResponseMessage>(component, props, streamingOptions).ConfigureAwait(false);
var result = await InvokeRenderTo<HttpResponseMessage>(component, props, streamingOptions)
.ConfigureAwait(false);

using var reader = new StreamReader(await result.Content.ReadAsStreamAsync().ConfigureAwait(false));

Expand All @@ -161,7 +168,7 @@
if (_config.UseCache)
{
var (success, cachedResult) = await _nodeJsService
.TryInvokeFromCacheAsync<string[]>(getAvailableComponentNames, args: new [] { _config.ScriptUrls })
.TryInvokeFromCacheAsync<string[]>(getAvailableComponentNames, args: new[] { _config.ScriptUrls })
.ConfigureAwait(false);

if (success)
Expand All @@ -173,7 +180,7 @@
using var stream = GetStreamFromEmbeddedScript(getAvailableComponentNames);

var result = await _nodeJsService.InvokeFromStreamAsync<string[]>(stream,
getAvailableComponentNames, args: new [] { _config.ScriptUrls })
getAvailableComponentNames, args: new[] { _config.ScriptUrls })
.ConfigureAwait(false);

return result!;
Expand All @@ -185,7 +192,8 @@

var manifestResourceName = $"Forte.Web.React.Scripts.{scriptName}";
var stream = currentAssembly.GetManifestResourceStream(manifestResourceName) ??
throw new InvalidOperationException($"Could not get manifest resource with name - {manifestResourceName}");
throw new InvalidOperationException(
$"Could not get manifest resource with name - {manifestResourceName}");

return stream;
}
Expand Down Expand Up @@ -219,7 +227,8 @@

private string CreateElement(Component component)
{
var element = $"React.createElement(window.__react.{component.Path}, window.{_config.NameOfObjectToSaveProps}[\"{component.JsonContainerId}\"])";
var element =
$"React.createElement(window.__react.{component.Path}, window.{_config.NameOfObjectToSaveProps}[\"{component.JsonContainerId}\"])";

return _config.StrictMode ? $"React.createElement(React.StrictMode, null, {element})" : element;
}
Expand Down Expand Up @@ -259,11 +268,12 @@
public RenderOptions() : this(RenderingMode.ClientAndServer, true)
{
}

public RenderOptions(bool serverOnly, bool enableStreaming = true) : this(serverOnly ? RenderingMode.Server : RenderingMode.ClientAndServer, enableStreaming)

public RenderOptions(bool serverOnly, bool enableStreaming = true) : this(
serverOnly ? RenderingMode.Server : RenderingMode.ClientAndServer, enableStreaming)
{
}

public RenderOptions(RenderingMode renderingMode, bool enableStreaming = true)
{
RenderingMode = renderingMode;
Expand Down
5 changes: 4 additions & 1 deletion Forte.Web.React/Scripts/renderToString.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
jsonContainerId,
props = {},
scriptFiles,
nameOfObjectToSaveProps
nameOfObjectToSaveProps,
nameOfGlobalDataToSave,
globalData = {}
) => {
scriptFiles.forEach((scriptFile) => {
require(scriptFile);
Expand All @@ -13,6 +15,7 @@
const ReactDOMServer = global["ReactDOMServer"];
const React = global["React"];
const componentRepository = global["__react"] || {};
global[nameOfGlobalDataToSave] = globalData;

const path = componentName.split(".");
let component = componentRepository[path[0]];
Expand Down
Loading