Update ReactGrid sample to use newer server-side rendering APIs

This commit is contained in:
SteveSandersonMS
2016-02-10 12:13:24 -08:00
parent 558aa31542
commit 71cb3abc13
9 changed files with 145 additions and 96 deletions

View File

@@ -1,3 +1,3 @@
{ {
presets: ["es2015", "react"] "presets": ["es2015", "react"]
} }

View File

@@ -1,9 +1,8 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import createBrowserHistory from 'history/lib/createBrowserHistory'; import { browserHistory } from 'react-router';
import ReactApp from './components/ReactApp.jsx'; import { ReactApp } from './components/ReactApp.jsx';
import 'bootstrap/dist/css/bootstrap.css'; import 'bootstrap/dist/css/bootstrap.css';
// In the browser, we render into a DOM node and hook up to the browser's history APIs // In the browser, we render into a DOM node and hook up to the browser's history APIs
var history = createBrowserHistory(); ReactDOM.render(<ReactApp history={ browserHistory } />, document.getElementById('react-app'));
ReactDOM.render(<ReactApp history={ history } />, document.getElementById('react-app'));

View File

@@ -0,0 +1,25 @@
import React from 'react';
import { renderToString } from 'react-dom/server';
import { match, RouterContext } from 'react-router';
import createMemoryHistory from 'history/lib/createMemoryHistory';
import { routes } from './components/ReactApp';
React;
export default function renderApp (params) {
return new Promise((resolve, reject) => {
// Match the incoming request against the list of client-side routes
match({ routes, location: params.location }, (error, redirectLocation, renderProps) => {
if (error) {
throw error;
}
// Build an instance of the application
const history = createMemoryHistory(params.url);
const app = <RouterContext {...renderProps} />;
// Render it as an HTML string which can be injected into the response
const html = renderToString(app);
resolve({ html });
});
});
}

View File

@@ -3,14 +3,16 @@ import { Router, Route } from 'react-router';
import { PeopleGrid } from './PeopleGrid.jsx'; import { PeopleGrid } from './PeopleGrid.jsx';
import { PersonEditor } from './PersonEditor.jsx'; import { PersonEditor } from './PersonEditor.jsx';
export default class ReactApp extends React.Component { export const routes = <Route>
<Route path="/" component={ PeopleGrid } />
<Route path="/:pageIndex" component={ PeopleGrid } />
<Route path="/edit/:personId" component={ PersonEditor } />
</Route>;
export class ReactApp extends React.Component {
render() { render() {
return ( return (
<Router history={this.props.history}> <Router history={this.props.history} children={ routes } />
<Route path="/" component={PeopleGrid} />
<Route path="/:pageIndex" component={PeopleGrid} />
<Route path="/edit/:personId" component={PersonEditor} />
</Router>
); );
} }
} }

View File

@@ -1,77 +1,77 @@
using Microsoft.AspNet.Builder; using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting; using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.SpaServices; using Microsoft.AspNet.SpaServices.Webpack;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.PlatformAbstractions; using Microsoft.Extensions.PlatformAbstractions;
namespace ReactExample namespace ReactExample
{ {
public class Startup public class Startup
{ {
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv) public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)
{ {
// Setup configuration sources. // Setup configuration sources.
var builder = new ConfigurationBuilder() var builder = new ConfigurationBuilder()
.SetBasePath(appEnv.ApplicationBasePath) .SetBasePath(appEnv.ApplicationBasePath)
.AddJsonFile("appsettings.json") .AddJsonFile("appsettings.json")
.AddEnvironmentVariables(); .AddEnvironmentVariables();
Configuration = builder.Build(); Configuration = builder.Build();
} }
public IConfigurationRoot Configuration { get; set; } public IConfigurationRoot Configuration { get; set; }
// This method gets called by the runtime. // This method gets called by the runtime.
public void ConfigureServices(IServiceCollection services) public void ConfigureServices(IServiceCollection services)
{ {
// Add MVC services to the services container. // Add MVC services to the services container.
services.AddMvc(); services.AddMvc();
} }
// Configure is called after ConfigureServices is called. // Configure is called after ConfigureServices is called.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{ {
loggerFactory.MinimumLevel = LogLevel.Warning; loggerFactory.MinimumLevel = LogLevel.Warning;
loggerFactory.AddConsole(); loggerFactory.AddConsole();
loggerFactory.AddDebug(); loggerFactory.AddDebug();
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
// Add the platform handler to the request pipeline. // Add the platform handler to the request pipeline.
app.UseIISPlatformHandler(); app.UseIISPlatformHandler();
// Add the following to the request pipeline only in development environment. // Add the following to the request pipeline only in development environment.
if (env.IsDevelopment()) if (env.IsDevelopment())
{ {
app.UseDeveloperExceptionPage(); app.UseDeveloperExceptionPage();
} }
else else
{ {
// Add Error handling middleware which catches all application specific errors and // Add Error handling middleware which catches all application specific errors and
// send the request to the following path or controller action. // send the request to the following path or controller action.
app.UseExceptionHandler("/Home/Error"); app.UseExceptionHandler("/Home/Error");
} }
// In dev mode, the JS/TS/etc is compiled and served dynamically and supports hot replacement. // In dev mode, the JS/TS/etc is compiled and served dynamically and supports hot replacement.
// In production, we assume you've used webpack to emit the prebuilt content to disk. // In production, we assume you've used webpack to emit the prebuilt content to disk.
if (env.IsDevelopment()) { if (env.IsDevelopment()) {
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions { app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions {
HotModuleReplacement = true, HotModuleReplacement = true,
ReactHotModuleReplacement = true ReactHotModuleReplacement = true
}); });
} }
// Add static files to the request pipeline. // Add static files to the request pipeline.
app.UseStaticFiles(); app.UseStaticFiles();
// Add MVC to the request pipeline. // Add MVC to the request pipeline.
app.UseMvc(routes => app.UseMvc(routes =>
{ {
routes.MapSpaFallbackRoute( routes.MapSpaFallbackRoute(
name: "default", name: "default",
defaults: new { controller="Home", action = "Index" }); defaults: new { controller="Home", action = "Index" });
}); });
} }
} }
} }

View File

@@ -1,4 +1,4 @@
<div id="react-app" asp-react-prerender-module="ReactApp/components/ReactApp.jsx"></div> <div id="react-app" asp-prerender-module="ReactApp/boot-server.jsx"></div>
@section scripts { @section scripts {
<script src="/dist/main.js"></script> <script src="/dist/main.js"></script>

View File

@@ -1,3 +1,3 @@
@using ReactExample @using ReactExample
@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers" @addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"
@addTagHelper "*, Microsoft.AspNet.ReactServices" @addTagHelper "*, Microsoft.AspNet.SpaServices"

View File

@@ -4,13 +4,14 @@
"dependencies": { "dependencies": {
"babel-core": "^6.4.5", "babel-core": "^6.4.5",
"bootstrap": "^3.3.5", "bootstrap": "^3.3.5",
"domain-task": "^1.0.0",
"formsy-react": "^0.17.0", "formsy-react": "^0.17.0",
"formsy-react-components": "^0.6.3", "formsy-react-components": "^0.6.3",
"griddle-react": "^0.2.14", "griddle-react": "^0.3.1",
"history": "^1.12.6", "history": "^1.12.6",
"react": "^0.14.0", "react": "^0.14.7",
"react-dom": "^0.14.0", "react-dom": "^0.14.7",
"react-router": "^1.0.0-rc3", "react-router": "^2.0.0-rc5",
"underscore": "^1.8.3" "underscore": "^1.8.3"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -8,17 +8,26 @@
// TODO: Consider some general method for checking if you have all the necessary NPM modules installed, // TODO: Consider some general method for checking if you have all the necessary NPM modules installed,
// and if not, giving an error that tells you what command to execute to install the missing ones. // and if not, giving an error that tells you what command to execute to install the missing ones.
var fs = require('fs'); var fs = require('fs');
var ts = require('ntypescript'); var ts = requireIfInstalled('ntypescript');
var babelCore = require('babel-core'); var babelCore = require('babel-core');
var resolveBabelRc = require('babel-loader/lib/resolve-rc'); // If this ever breaks, we can easily scan up the directory hierarchy ourselves var resolveBabelRc = require('babel-loader/lib/resolve-rc'); // If this ever breaks, we can easily scan up the directory hierarchy ourselves
var origJsLoader = require.extensions['.js']; var origJsLoader = require.extensions['.js'];
function resolveBabelOptions(relativeToFilename) { function resolveBabelOptions(relativeToFilename) {
var babelRcText = resolveBabelRc(relativeToFilename); var babelRcText = resolveBabelRc(relativeToFilename);
return babelRcText ? JSON.parse(babelRcText) : {}; try {
return babelRcText ? JSON.parse(babelRcText) : {};
} catch (ex) {
ex.message = 'Error while parsing babelrc JSON: ' + ex.message;
throw ex;
}
} }
function loadViaTypeScript(module, filename) { function loadViaTypeScript(module, filename) {
if (!ts) {
throw new Error('Can\'t load .ts/.tsx files because the \'ntypescript\' package isn\'t installed.\nModule requested: ' + module);
}
// First perform a minimal transpilation from TS code to ES2015. This is very fast (doesn't involve type checking) // First perform a minimal transpilation from TS code to ES2015. This is very fast (doesn't involve type checking)
// and is unlikely to need any special compiler options // and is unlikely to need any special compiler options
var src = fs.readFileSync(filename, 'utf8'); var src = fs.readFileSync(filename, 'utf8');
@@ -45,6 +54,19 @@ function loadViaBabel(module, filename) {
} }
} }
function requireIfInstalled(packageName) {
return isPackageInstalled(packageName) ? require(packageName) : null;
}
function isPackageInstalled(packageName) {
try {
require.resolve(packageName);
return true;
} catch(e) {
return false;
}
}
function register() { function register() {
require.extensions['.js'] = loadViaBabel; require.extensions['.js'] = loadViaBabel;
require.extensions['.jsx'] = loadViaBabel; require.extensions['.jsx'] = loadViaBabel;