From eac76683cc118d208577f16d5bf52f947b9c92d9 Mon Sep 17 00:00:00 2001 From: SteveSandersonMS Date: Wed, 24 Feb 2016 11:02:21 +0000 Subject: [PATCH] Enable server-side rendering for ReactSpa template --- .../ClientApp/{boot.tsx => boot-client.tsx} | 2 ++ templates/ReactSpa/ClientApp/boot-server.tsx | 34 +++++++++++++++++++ templates/ReactSpa/Views/Home/Index.cshtml | 2 +- templates/ReactSpa/Views/_ViewImports.cshtml | 1 + templates/ReactSpa/package.json | 2 ++ templates/ReactSpa/webpack.config.js | 2 +- 6 files changed, 41 insertions(+), 2 deletions(-) rename templates/ReactSpa/ClientApp/{boot.tsx => boot-client.tsx} (71%) create mode 100644 templates/ReactSpa/ClientApp/boot-server.tsx diff --git a/templates/ReactSpa/ClientApp/boot.tsx b/templates/ReactSpa/ClientApp/boot-client.tsx similarity index 71% rename from templates/ReactSpa/ClientApp/boot.tsx rename to templates/ReactSpa/ClientApp/boot-client.tsx index 3625bef..7d1e6bf 100644 --- a/templates/ReactSpa/ClientApp/boot.tsx +++ b/templates/ReactSpa/ClientApp/boot-client.tsx @@ -6,6 +6,8 @@ import * as ReactDOM from 'react-dom'; import { browserHistory, Router } from 'react-router'; import { routes } from './routes'; +// This code starts up the React app when it runs in a browser. It sets up the routing configuration +// and injects the app into a DOM element. ReactDOM.render( , document.getElementById('react-app') diff --git a/templates/ReactSpa/ClientApp/boot-server.tsx b/templates/ReactSpa/ClientApp/boot-server.tsx new file mode 100644 index 0000000..b433f2e --- /dev/null +++ b/templates/ReactSpa/ClientApp/boot-server.tsx @@ -0,0 +1,34 @@ +import * as React from 'react'; +import { renderToString } from 'react-dom/server'; +import { match, RouterContext } from 'react-router'; +import createMemoryHistory from 'history/lib/createMemoryHistory'; +import { routes } from './routes'; + +// The 'asp-prerender-module' tag helper invokes the following function when the React app is to +// be prerendered on the server. It runs asynchronously, and issues a callback with the React app's +// initial HTML and any other state variables. + +export default function (params: any): Promise<{ html: string }> { + return new Promise<{ html: string, globals: { [key: string]: any } }>((resolve, reject) => { + // Match the incoming request against the list of client-side routes, and reject if there was no match + match({ routes, location: params.location }, (error, redirectLocation, renderProps: any) => { + if (error) { + throw error; + } + + // Build an instance of the application and perform an initial render. + // This will cause any async tasks (e.g., data access) to begin. + const history = createMemoryHistory(params.url); + const app = ; + renderToString(app); + + // Once the tasks are done, we can perform the final render + params.domainTasks.then(() => { + resolve({ + html: renderToString(app), + globals: { /* Supply any other JSON-serializable data you want to make available on the client */ } + }); + }, reject); // Also propagate any errors back into the host application + }); + }); +} diff --git a/templates/ReactSpa/Views/Home/Index.cshtml b/templates/ReactSpa/Views/Home/Index.cshtml index 139ed8a..1bb189e 100644 --- a/templates/ReactSpa/Views/Home/Index.cshtml +++ b/templates/ReactSpa/Views/Home/Index.cshtml @@ -2,7 +2,7 @@ ViewData["Title"] = "Home Page"; } -
Loading...
+
Loading...
@section scripts { diff --git a/templates/ReactSpa/Views/_ViewImports.cshtml b/templates/ReactSpa/Views/_ViewImports.cshtml index 7252adc..3d6c9a3 100755 --- a/templates/ReactSpa/Views/_ViewImports.cshtml +++ b/templates/ReactSpa/Views/_ViewImports.cshtml @@ -1,2 +1,3 @@ @using WebApplicationBasic @addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers" +@addTagHelper "*, Microsoft.AspNet.SpaServices" diff --git a/templates/ReactSpa/package.json b/templates/ReactSpa/package.json index 283808b..3199313 100644 --- a/templates/ReactSpa/package.json +++ b/templates/ReactSpa/package.json @@ -13,6 +13,7 @@ "extract-text-webpack-plugin": "^1.0.1", "file-loader": "^0.8.5", "jquery": "^2.2.1", + "ntypescript": "^1.201602232306.1", "react-transform-hmr": "^1.0.2", "style-loader": "^0.13.0", "ts-loader": "^0.8.1", @@ -24,6 +25,7 @@ }, "dependencies": { "babel-core": "^6.5.2", + "domain-task": "^1.0.0", "react": "^0.14.7", "react-dom": "^0.14.7", "react-router": "^2.0.0" diff --git a/templates/ReactSpa/webpack.config.js b/templates/ReactSpa/webpack.config.js index 44fbddd..503fea9 100644 --- a/templates/ReactSpa/webpack.config.js +++ b/templates/ReactSpa/webpack.config.js @@ -17,7 +17,7 @@ module.exports = merge({ ] }, entry: { - main: ['./ClientApp/boot.tsx'], + main: ['./ClientApp/boot-client.tsx'], vendor: ['bootstrap', 'bootstrap/dist/css/bootstrap.css', 'style-loader', 'jquery'] }, output: {