Skip to content

Commit 2947e1f

Browse files
Modernize .NET Core sample
1 parent c3cfee8 commit 2947e1f

File tree

13 files changed

+6404
-1140
lines changed

13 files changed

+6404
-1140
lines changed
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
{
2-
"presets": ["react", "env"]
2+
"presets": ["@babel/preset-react", "@babel/preset-env"],
3+
"plugins": [
4+
"@babel/proposal-object-rest-spread",
5+
"@babel/proposal-class-properties"
6+
]
37
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright (c) 2014-Present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
10+
body {
11+
font-family: Calibri, Verdana, sans-serif;
12+
}
13+
14+
.commentList {
15+
list-style-type: none;
16+
margin: 0;
17+
padding: 0;
18+
}
19+
20+
.commentList li {
21+
border-bottom: 1px solid #999;
22+
padding: 0.5em 0;
23+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/**
2+
* Copyright (c) 2014-Present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
10+
import React from 'react';
11+
import PropTypes from 'prop-types';
12+
import * as Reactstrap from 'reactstrap';
13+
14+
export function CommentsBox(props) {
15+
let [state, updateState] = React.useState({
16+
comments: props.initialComments,
17+
page: props.page,
18+
hasMore: true,
19+
loadingMore: false,
20+
});
21+
22+
function loadMoreClicked(evt) {
23+
let nextPage = state.page + 1;
24+
let comments = state.comments;
25+
updateState(prevState => ({
26+
...prevState,
27+
page: nextPage,
28+
loadingMore: true,
29+
}));
30+
31+
let url = '/comments/page-' + (state.page + 1);
32+
let xhr = new XMLHttpRequest();
33+
xhr.open('GET', url, true);
34+
xhr.setRequestHeader('Content-Type', 'application/json');
35+
36+
xhr.onload = () => {
37+
let data = JSON.parse(xhr.responseText);
38+
updateState(prevState => ({
39+
...prevState,
40+
comments: comments.concat(data.comments),
41+
hasMore: data.hasMore,
42+
loadingMore: false,
43+
}));
44+
};
45+
xhr.send();
46+
evt.preventDefault();
47+
}
48+
49+
let commentNodes = state.comments.map(comment => (
50+
<Comment author={comment.author}>{comment.text}</Comment>
51+
));
52+
53+
function renderMoreLink() {
54+
if (state.loadingMore) {
55+
return <em>Loading...</em>;
56+
} else if (state.hasMore) {
57+
return (
58+
<Reactstrap.Button onClick={loadMoreClicked}>
59+
Load More
60+
</Reactstrap.Button>
61+
);
62+
} else {
63+
return <em>No more comments</em>;
64+
}
65+
}
66+
67+
return (
68+
<div>
69+
<p className="lead">
70+
This is an example of ReactJS.NET's server-side rendering. The
71+
initial state of this comments box is rendered server-side, and
72+
additional data is loaded via AJAX and rendered client-side.
73+
</p>
74+
<div className="comments">
75+
<h1>Comments</h1>
76+
<ol className="commentList">{commentNodes}</ol>
77+
{renderMoreLink()}
78+
<hr />
79+
</div>
80+
</div>
81+
);
82+
}
83+
84+
class Comment extends React.Component {
85+
static propTypes = {
86+
author: PropTypes.object.isRequired,
87+
};
88+
89+
render() {
90+
return (
91+
<li>
92+
<Avatar author={this.props.author} />
93+
<strong>{this.props.author.name}</strong>
94+
{': '}
95+
{this.props.children}
96+
</li>
97+
);
98+
}
99+
}
100+
101+
class Avatar extends React.Component {
102+
static propTypes = {
103+
author: PropTypes.object.isRequired,
104+
};
105+
106+
render() {
107+
return (
108+
<img
109+
src={this.getPhotoUrl(this.props.author)}
110+
alt={'Photo of ' + this.props.author.name}
111+
width={50}
112+
height={50}
113+
className="commentPhoto mr-1"
114+
/>
115+
);
116+
}
117+
118+
getPhotoUrl = author => {
119+
return (
120+
'https://avatars.githubusercontent.com/' +
121+
author.githubUsername +
122+
'?s=50'
123+
);
124+
};
125+
}

src/React.Sample.Webpack.CoreMvc/Content/components/home.jsx

Lines changed: 43 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, Fragment, useState } from 'react';
1+
import { Component, Fragment } from 'react';
22
import {
33
Link,
44
BrowserRouter,
@@ -9,6 +9,7 @@ import {
99
} from 'react-router-dom';
1010
import { Helmet } from 'react-helmet';
1111

12+
import { CommentsBox } from './comments/Sample.jsx';
1213
import { StyledComponentsDemo } from './styled-components.jsx';
1314
import { EmotionDemo } from './emotion.jsx';
1415
import { ReactJssDemo } from './react-jss.jsx';
@@ -20,6 +21,9 @@ class Navbar extends Component {
2021
<li>
2122
<Link to="/">Home</Link>
2223
</li>
24+
<li>
25+
<Link to="/comments">Comments Demo</Link>
26+
</li>
2327
<li>
2428
<Link to="/styled-components">Styled Components Demo</Link>
2529
</li>
@@ -34,33 +38,14 @@ class Navbar extends Component {
3438
}
3539
}
3640

37-
function HooksDemo() {
38-
let [count, updateCount] = React.useState(0);
39-
return (
40-
<button onClick={() => updateCount(count + 1)}>
41-
Click count: {count}
42-
</button>
43-
);
44-
}
45-
4641
class HomePage extends Component {
4742
render() {
4843
return (
4944
<Fragment>
5045
<Helmet>
5146
<title>ReactJS.NET Demos</title>
5247
</Helmet>
53-
<h1
54-
style={{
55-
lineHeight: '2',
56-
color: '#222',
57-
fontFamily: 'Helvetica, sans-serif',
58-
textShadow: '0 0 5px lightgray',
59-
}}
60-
>
61-
ReactJS.NET is 🔥🔥
62-
</h1>
63-
<HooksDemo />
48+
ReactJS.NET is 🔥🔥
6449
</Fragment>
6550
);
6651
}
@@ -69,31 +54,44 @@ class HomePage extends Component {
6954
export default class HomeComponent extends Component {
7055
render() {
7156
const app = (
72-
<Fragment>
73-
<Navbar />
74-
<Switch>
75-
<Route
76-
exact
77-
path="/"
78-
render={() => <Redirect to="/home" />}
79-
/>
80-
<Route path="/home" component={HomePage} />
81-
<Route
82-
path="/styled-components"
83-
component={StyledComponentsDemo}
84-
/>
85-
<Route path="/react-jss" component={ReactJssDemo} />
86-
<Route path="/emotion" component={EmotionDemo} />
87-
<Route
88-
path="*"
89-
component={({ staticContext }) => {
90-
if (staticContext) staticContext.status = 404;
57+
<div className="container">
58+
<div className="jumbotron">
59+
<h1 className="display-4">.NET Core Sample</h1>
60+
<Navbar />
61+
<hr className="my-4" />
62+
<Switch>
63+
<Route
64+
exact
65+
path="/"
66+
render={() => <Redirect to="/home" />}
67+
/>
68+
<Route path="/home" component={HomePage} />
69+
<Route
70+
path="/comments"
71+
component={() => (
72+
<CommentsBox
73+
initialComments={this.props.initialComments}
74+
page={this.props.page}
75+
/>
76+
)}
77+
/>
78+
<Route
79+
path="/styled-components"
80+
component={StyledComponentsDemo}
81+
/>
82+
<Route path="/react-jss" component={ReactJssDemo} />
83+
<Route path="/emotion" component={EmotionDemo} />
84+
<Route
85+
path="*"
86+
component={({ staticContext }) => {
87+
if (staticContext) staticContext.status = 404;
9188

92-
return <h1>Not Found :(</h1>;
93-
}}
94-
/>
95-
</Switch>
96-
</Fragment>
89+
return <h1>Not Found :(</h1>;
90+
}}
91+
/>
92+
</Switch>
93+
</div>
94+
</div>
9795
);
9896

9997
if (typeof window === 'undefined') {
Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,91 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
13
using Microsoft.AspNetCore.Mvc;
24

35
namespace React.Sample.Webpack.CoreMvc.Controllers
46
{
57
public class HomeController : Controller
68
{
7-
public IActionResult Index()
9+
private const int COMMENTS_PER_PAGE = 3;
10+
11+
private readonly IDictionary<string, AuthorModel> _authors;
12+
private readonly IList<CommentModel> _comments;
13+
14+
public HomeController()
15+
{
16+
// In reality, you would use a repository or something for fetching data
17+
// For clarity, we'll just use a hard-coded list.
18+
_authors = new Dictionary<string, AuthorModel>
19+
{
20+
{"daniel", new AuthorModel { Name = "Daniel Lo Nigro", GithubUsername = "Daniel15" }},
21+
{"vjeux", new AuthorModel { Name = "Christopher Chedeau", GithubUsername = "vjeux" }},
22+
{"cpojer", new AuthorModel { Name = "Christoph Pojer", GithubUsername = "cpojer" }},
23+
{"jordwalke", new AuthorModel { Name = "Jordan Walke", GithubUsername = "jordwalke" }},
24+
{"zpao", new AuthorModel { Name = "Paul O'Shannessy", GithubUsername = "zpao" }},
25+
};
26+
_comments = new List<CommentModel>
27+
{
28+
new CommentModel { Author = _authors["daniel"], Text = "First!!!!111!" },
29+
new CommentModel { Author = _authors["zpao"], Text = "React is awesome!" },
30+
new CommentModel { Author = _authors["cpojer"], Text = "Awesome!" },
31+
new CommentModel { Author = _authors["vjeux"], Text = "Hello World" },
32+
new CommentModel { Author = _authors["daniel"], Text = "Foo" },
33+
new CommentModel { Author = _authors["daniel"], Text = "Bar" },
34+
new CommentModel { Author = _authors["daniel"], Text = "FooBarBaz" },
35+
};
36+
}
37+
38+
public ActionResult Index()
39+
{
40+
return View(new IndexViewModel
41+
{
42+
Comments = _comments.Take(COMMENTS_PER_PAGE).ToList().AsReadOnly(),
43+
CommentsPerPage = COMMENTS_PER_PAGE,
44+
Page = 1
45+
});
46+
}
47+
48+
public ActionResult Comments(int page)
49+
{
50+
var comments = _comments.Skip((page - 1) * COMMENTS_PER_PAGE).Take(COMMENTS_PER_PAGE);
51+
var hasMore = page * COMMENTS_PER_PAGE < _comments.Count;
52+
53+
if (ControllerContext.HttpContext.Request.ContentType == "application/json")
54+
{
55+
return new JsonResult(new
56+
{
57+
comments = comments,
58+
hasMore = hasMore
59+
});
60+
}
61+
else
62+
{
63+
return View("~/Views/Home/Index.cshtml", new IndexViewModel
64+
{
65+
Comments = _comments.Take(COMMENTS_PER_PAGE * page).ToList().AsReadOnly(),
66+
CommentsPerPage = COMMENTS_PER_PAGE,
67+
Page = page
68+
});
69+
}
70+
}
71+
72+
public class AuthorModel
73+
{
74+
public string Name { get; set; }
75+
public string GithubUsername { get; set; }
76+
}
77+
78+
public class CommentModel
79+
{
80+
public AuthorModel Author { get; set; }
81+
public string Text { get; set; }
82+
}
83+
84+
public class IndexViewModel
885
{
9-
return View();
86+
public IReadOnlyList<CommentModel> Comments { get; set; }
87+
public int CommentsPerPage { get; set; }
88+
public int Page { get; set; }
1089
}
1190
}
1291
}

src/React.Sample.Webpack.CoreMvc/Startup.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
5959
app.UseMvc(routes =>
6060
{
6161
routes.MapRoute("default", "{path?}", new { controller = "Home", action = "Index" });
62+
routes.MapRoute("comments", "comments/page-{page}", new { controller = "Home", action = "Comments" });
6263
});
6364
}
6465
}

0 commit comments

Comments
 (0)