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

React Router Support #407

Merged
merged 12 commits into from
Jul 22, 2017
File renamed without changes.
1 change: 1 addition & 0 deletions build.proj
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ of patent rights can be found in the PATENTS file in the same directory.
<PackageAssemblies Include="React.Core" />
<PackageAssemblies Include="React.MSBuild" />
<PackageAssemblies Include="React.Owin" />
<PackageAssemblies Include="React.Router" />
<PackageAssemblies Include="React.Web" />
<PackageAssemblies Include="React.Web.Mvc4" />
<PackageAssemblies Include="System.Web.Optimization.React" />
Expand Down
17 changes: 1 addition & 16 deletions src/React.AspNet/HtmlHelperExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,7 @@ private static IReactEnvironment Environment
{
get
{
try
{
return ReactEnvironment.Current;
}
catch (TinyIoCResolutionException ex)
{
throw new ReactNotInitialisedException(
#if LEGACYASPNET
"ReactJS.NET has not been initialised correctly.",
#else
"ReactJS.NET has not been initialised correctly. Please ensure you have " +
"called services.AddReact() and app.UseReact() in your Startup.cs file.",
#endif
ex
);
}
return ReactEnvironment.GetCurrentOrThrow;
}
}

Expand Down
8 changes: 8 additions & 0 deletions src/React.Core/IReactEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@ public interface IReactEnvironment
/// <returns>The component</returns>
IReactComponent CreateComponent<T>(string componentName, T props, string containerId = null, bool clientOnly = false);

/// <summary>
/// Adds the provided <see cref="IReactComponent"/> to the list of components to render client side.
/// </summary>
/// <param name="component">Component to add to client side render list</param>
/// <param name="clientOnly">True if server-side rendering will be bypassed. Defaults to false.</param>
/// <returns>The component</returns>
IReactComponent CreateComponent(IReactComponent component, bool clientOnly = false);

/// <summary>
/// Renders the JavaScript required to initialise all components client-side. This will
/// attach event handlers to the server-rendered HTML.
Expand Down
2 changes: 1 addition & 1 deletion src/React.Core/ReactComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ public virtual string RenderHtml(bool renderContainerOnly = false, bool renderSe
attributes,
html,
ContainerTag
);
);
}
catch (JsRuntimeException ex)
{
Expand Down
46 changes: 46 additions & 0 deletions src/React.Core/ReactEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using JSPool;
using Newtonsoft.Json;
using React.Exceptions;
using React.TinyIoC;

namespace React
{
Expand Down Expand Up @@ -85,6 +86,34 @@ public static IReactEnvironment Current
get { return AssemblyRegistration.Container.Resolve<IReactEnvironment>(); }
}

/// <summary>
/// Gets the <see cref="IReactEnvironment"/> for the current request. If no environment
/// has been created for the current request, creates a new one.
/// Also provides more specific error information in the event that ReactJS.NET is misconfigured.
/// </summary>
public static IReactEnvironment GetCurrentOrThrow
{
get
{
try
{
return Current;
}
catch (TinyIoCResolutionException ex)
{
throw new ReactNotInitialisedException(
#if NET451
"ReactJS.NET has not been initialised correctly.",
#else
"ReactJS.NET has not been initialised correctly. Please ensure you have " +
"called services.AddReact() and app.UseReact() in your Startup.cs file.",
#endif
ex
);
}
}
}

/// <summary>
/// Initializes a new instance of the <see cref="ReactEnvironment"/> class.
/// </summary>
Expand Down Expand Up @@ -274,6 +303,23 @@ public virtual IReactComponent CreateComponent<T>(string componentName, T props,
return component;
}

/// <summary>
/// Adds the provided <see cref="IReactComponent"/> to the list of components to render client side.
/// </summary>
/// <param name="component">Component to add to client side render list</param>
/// <param name="clientOnly">True if server-side rendering will be bypassed. Defaults to false.</param>
/// <returns>The component</returns>
public virtual IReactComponent CreateComponent(IReactComponent component, bool clientOnly = false)
{
if (!clientOnly)
{
EnsureUserScriptsLoaded();
}

_components.Add(component);
return component;
}

/// <summary>
/// Renders the JavaScript required to initialise all components client-side. This will
/// attach event handlers to the server-rendered HTML.
Expand Down
28 changes: 28 additions & 0 deletions src/React.Core/Resources/shims.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,31 @@ function ReactNET_initReact() {
// :'(
return false;
}

/**
* Polyfill for engines that do not support Object.assign
*/
if (typeof Object.assign !== 'function') {
Object.assign = function (target, varArgs) { // .length of function is 2
'use strict';
if (target == null) { // TypeError if undefined or null
throw new TypeError('Cannot convert undefined or null to object');
}

var to = Object(target);

for (var index = 1; index < arguments.length; index++) {
var nextSource = arguments[index];

if (nextSource != null) { // Skip over if undefined or null
for (var nextKey in nextSource) {
// Avoid bugs when hasOwnProperty is shadowed
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
};
}
9 changes: 9 additions & 0 deletions src/React.Router/Content/Views/web.config.transform
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<configuration>
<system.web.webPages.razor>
<pages>
<namespaces>
<add namespace="React.Router" />
</namespaces>
</pages>
</system.web.webPages.razor>
</configuration>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The react router htmlhelper lives in this namespace. I seem to recall being unable to use it if I didn't either have this statement in my web.config or an explicit using statement in file.
You have a similar file in the mvc4 project.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, interesting.

28 changes: 28 additions & 0 deletions src/React.Router/ExecutionResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (c) 2014-Present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

namespace React.Router
{
/// <summary>
/// Contains the context object used during execution in addition to
/// the string result of rendering the React Router component.
/// </summary>
public class ExecutionResult
{
/// <summary>
/// String result of ReactDOMServer render of provided component.
/// </summary>
public string RenderResult { get; set; }

/// <summary>
/// Context object used during JS engine execution.
/// </summary>
public RoutingContext Context { get; set; }
}
}
119 changes: 119 additions & 0 deletions src/React.Router/HtmlHelperExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Copyright (c) 2014-Present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

using System;
using React.Exceptions;
using React.TinyIoC;

#if NET451
using System.Web;
using System.Web.Mvc;
using HttpResponse = System.Web.HttpResponseBase;
using IHtmlHelper = System.Web.Mvc.HtmlHelper;
#else
using Microsoft.AspNetCore.Mvc.Rendering;
using IHtmlString = Microsoft.AspNetCore.Html.IHtmlContent;
using HttpResponse = Microsoft.AspNetCore.Http.HttpResponse;
using Microsoft.AspNetCore.Html;
#endif

namespace React.Router
{
/// <summary>
/// Render a React StaticRouter Component with context.
/// </summary>
public static class HtmlHelperExtensions
{
/// <summary>
/// Gets the React environment
/// </summary>
private static IReactEnvironment Environment
{
get
{
return ReactEnvironment.GetCurrentOrThrow;
}
}

/// <summary>
/// Render a React StaticRouter Component with context object.
/// Can optionally be provided with a custom context handler to handle the various status codes.
/// </summary>
/// <param name="htmlHelper">MVC Razor <see cref="IHtmlHelper"/></param>
/// <param name="componentName">Name of React Static Router component. Expose component globally to ReactJS.NET</param>
/// <param name="props">Props to initialise the component with</param>
/// <param name="path">F.x. from Request.Path. Used by React Static Router to determine context and routing.</param>
/// <param name="contextHandler">Optional custom context handler, can be used instead of providing a Response object</param>
/// <param name="htmlTag">HTML tag to wrap the component in. Defaults to &lt;div&gt;</param>
/// <param name="containerId">ID to use for the container HTML tag. Defaults to an auto-generated ID</param>
/// <param name="clientOnly">Skip rendering server-side and only output client-side initialisation code. Defaults to <c>false</c></param>
/// <param name="serverOnly">Skip rendering React specific data-attributes during server side rendering. Defaults to <c>false</c></param>
/// <param name="containerClass">HTML class(es) to set on the container tag</param>
/// <returns><see cref="IHtmlString"/> containing the rendered markup for provided React Router component</returns>
public static IHtmlString ReactRouterWithContext<T>(
this IHtmlHelper htmlHelper,
string componentName,
T props,
string path = null,
string htmlTag = null,
string containerId = null,
bool clientOnly = false,
bool serverOnly = false,
string containerClass = null,
Action<HttpResponse, RoutingContext> contextHandler = null
)
{
try
{
var response = htmlHelper.ViewContext.HttpContext.Response;
path = path ?? htmlHelper.ViewContext.HttpContext.Request.Path;

var reactComponent
= Environment.CreateRouterComponent(
componentName,
props,
path,
containerId,
clientOnly
);

if (!string.IsNullOrEmpty(htmlTag))
{
reactComponent.ContainerTag = htmlTag;
}
if (!string.IsNullOrEmpty(containerClass))
{
reactComponent.ContainerClass = containerClass;
}

var executionResult = reactComponent.RenderRouterWithContext(clientOnly, serverOnly);

if (executionResult.Context?.status != null)
{
// Use provided contextHandler
if (contextHandler != null)
{
contextHandler(response, executionResult.Context);
}
// Handle routing context internally
else
{
SetServerResponse.ModifyResponse(executionResult.Context, response);
}
}

return new HtmlString(executionResult.RenderResult);
}
finally
{
Environment.ReturnEngineToPool();
}
}
}
}
7 changes: 7 additions & 0 deletions src/React.Router/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using System.Reflection;
using System.Runtime.InteropServices;

[assembly: AssemblyTitle("React.Router")]
[assembly: AssemblyDescription("React Router support for ReactJS.NET")]
[assembly: ComVisible(false)]
[assembly: Guid("277850fc-8765-4042-945f-a50b8f2525a9")]
54 changes: 54 additions & 0 deletions src/React.Router/React.Router.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Description>React Router support for ReactJS.NET.</Description>
<Copyright>Copyright 2014-Present Facebook, Inc</Copyright>
<AssemblyTitle>ReactJS.NET Router</AssemblyTitle>
<Authors>Daniel Lo Nigro, Gunnar Már Óttarsson</Authors>
<TargetFrameworks>net451;netstandard1.6</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>React.Router</AssemblyName>
<AssemblyOriginatorKeyFile>../key.snk</AssemblyOriginatorKeyFile>
<SignAssembly>true</SignAssembly>
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
<PackageId>React.Router</PackageId>
<PackageTags>asp.net;mvc;asp;javascript;js;react;facebook;reactjs;babel;router;react router</PackageTags>
<PackageIconUrl>http://reactjs.net/img/logo_64.png</PackageIconUrl>
<PackageProjectUrl>http://reactjs.net/</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/reactjs/React.NET#licence</PackageLicenseUrl>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net451|AnyCPU'">
<DefineConstants>TRACE;DEBUG;ASPNETCORE;NET451</DefineConstants>
</PropertyGroup>

<ItemGroup>
<Compile Include="..\SharedAssemblyInfo.cs" />
<Compile Include="..\SharedAssemblyVersionInfo.cs" />
<Content Include="Content\**\*">
<Pack>true</Pack>
<PackagePath>content\</PackagePath>
</Content>
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.6' ">
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="1.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.ViewFeatures" Version="1.0.3" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'net451' ">
<Reference Include="System.Web" />
<Reference Include="Microsoft.CSharp" />
<PackageReference Include="Microsoft.AspNet.Mvc" Version="4.0.20710" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\React.Core\React.Core.csproj" />
</ItemGroup>

<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

</Project>
Loading