React was Facebook's answer to helping more people work on the Facebook code base and deliver features quicker. React worked so well for Facebook that they eventually open sourced it Github.com . Today React is a mature library for building component-based frontends (client-side code that runs in the browser). It is extremely popular and has a massive – and growing – community and ecosystem.
ASP.NET Core was first released in 2016 and is now a mature open source and cross-platform web application framework. An excellent choice for building backends (application code that runs on the server) that interact with databases such as SQL Server, it also works well in cloud platforms such as Microsoft Azure.
In this overview we'll show you how to create a single-page application (SPA) using ASP.NET Core and React via the standard template in Visual Studio. You'll learn where the entry points of both the ASP.NET Core and React apps are and how they integrate with each other. You'll also learn how Visual Studio runs both the frontend and backend together in development mode, as well as how it packages them up, ready for production. By the end, you'll have gained fundamental knowledge that you can apply to building your own apps using these awesome technologies.
Technical requirements
The tools we’ll be using are:
• Visual Studio 2019: This can be downloaded and installed from https://visualstudio.microsoft.com/vs/ . Make sure that the following features are selected in the installer:
a) ASP.NET and web development
b) Azure development
c) Node.js development
• .NET 5.0: This can be downloaded from https://dotnet.microsoft.com/download/dotnet/5.0 .
• Node.js and npm: These can be downloaded from https://nodejs.org/ .
Check out the following video to see the code in action:
http://bit.ly/2ZpsqaZ
SPA architecture :
An SPA is a web app that loads a single HTML page that is dynamically updated by JavaScript as the user interacts with the app. Imagine a simple sign-up form where a user can enter a name and an email address. Once the user fills out and submits the form, a whole page refresh doesn't occur. Instead, some JavaScript in the browser handles the form submission with an HTTP POST request and then updates the page with the result of the request. Refer to the following diagram:
Figure 1.1: Form in a SPA
So, after the first HTTP request that returns the single HTML page, subsequent HTTP requests are only for data and not HTML markup. All the pages are rendered in the client's browser by JavaScript.
To build our SPA we'll use React to render our frontend and ASP.NET Core for the backend API.
Understanding the backend
We are going to start by creating an ASP.NET Core and React app using the standard template in Visual Studio. This template is perfect for us to review and understand basic backend components in an ASP.NET Core SPA.
Creating an ASP.NET Core and React templated app
Let's open Visual Studio and carry out the following steps to create our templated app:
In the start-up dialog, choose Create a new project:
Figure 1.2: Visual Studio Start-up dialogNext, choose ASP.NET Core Web Application in the wizard that opens and click the Next button:
Figure 1.3: Creating a new web app in Visual StudioGive the project a name of your choice and choose an appropriate location to save the project to. Click the Create button to create the project:
Figure 1.4: Specifying the new project name and location
Another dialog will appear that allows us to specify the version of ASP.NET Core we want to use, as well as the specific type of project we want to create.
- Select ASP.NET Core 5.0 as the version and React.js in the dialog, and then click the Create button, which will create the project: Figure 1.5: The project template and ASP.NET Core version
Important note
If ASP.NET Core 5.0 isn't listed, make sure the latest version of Visual Studio is installed. This can be done by choosing the Check for Updates option on the Help menu.
- Now that the project has been created, press F5 to run the app. After a minute or so, the app will appear in a browser: Figure 1.6: The home page of the app
We'll find out later why the app took so long to run the first time. Great—we've created the ASP.NET Core React SPA. Now, let's inspect the backend code.
Understanding the backend entry point
An ASP.NET Core app is a console app that creates a web server. The entry point for the app is a method called Main in a class called Program, which can be found in the Program.cs file in the root of the project:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup();
});
}
This method creates a web host using Host.CreateDefaultBuilder, which configures items such as the following:
• The location of the root of the web content
• Where the settings are for items such as the database connection string
• The logging level and where the logs are output
We can override the default builder using fluent APIs, which start with Use. For example, to adjust the root of the web content, we can add the highlighted line in the following snippet:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseContentRoot("some-path");
webBuilder.UseStartup();
});
The last thing that is specified in the builder is the Startup class, which we'll look at in the following section.
Understanding the Startup class
The Startup class is found in Startup.cs and configures the services that the app uses, as well as the request/response pipeline.
The ConfigureServices method
Services are configured using a method called ConfigureServices. This method is used to register items such as the following:
• Our controllers that will handle requests
• Our authorization policies
• Our CORS policies
• Our own classes that need to be available in dependency injection
Services are added by calling methods on the services parameter and, generally, start with Add. Notice the call to the AddSpaStaticFiles method in the following code snippet:
public void ConfigureServices(IServiceCollection services)
{0
services.AddControllersWithViews();
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/build";
});
}
This is a key part of how the React app is integrated into ASP.NET Core in production, because this specifies the location of the React app.
Important note
It is important to understand that the ASP.NET Core app runs on the server, with the React app running on the client in the browser. The ASP.NET Core app simply serves the files in the ClientApp/Build folder without any interpretation or manipulation.
The ClientApp/Build files are only used in production mode, though. Next we'll find out how the React app is integrated into ASP.NET Core in development mode.
The Configure method
When a request comes into ASP.NET Core, it goes through what is called the request/response pipeline, where some middleware code is executed. This pipeline is configured using a method called Configure. It is this method we will use to define exactly which middleware is executed and in what order. Middleware code is invoked by methods that generally start with Use in the app parameter. So, we would typically specify middleware such as authentication early in the Configure method, and MVC middleware toward the end. The pipeline that the template created is as follows:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseRouting();
app.UseEndpoints( ... );
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
}
Notice that a method called UseSpaStaticFiles is called in the pipeline, just before the routing and endpoints are set up. This allows the host to serve the React app, as well as the web API.
Also, notice that a UseSpa method is called after the endpoint middleware. This is the middleware that will handle requests to the React app, which will simply serve the single page in the React app. It is placed after UseEndpoints so that requests to the web API take precedence over requests to the React app.
The UseSpa method has a parameter that is actually a function that executes when the app is first run. This function contains a branch of logic that calls spa.UseReactDevelopmentServer(npmScript: "start") if you're in development mode. This tells ASP.NET Core to use a development server by running npm start. We'll delve into the npm start command a little later. So, in development mode, the React app will be run on a development server rather than ASP.NET Core serving the files from ClientApp/Build. We'll learn more about this development server later.
Custom middleware
We can create our own middleware using a class such as the following one. This middleware logs information about every single request that is handled by the ASP.NET Core app:
public class CustomLogger
{
private readonly RequestDelegate _next;
public CustomLogger(RequestDelegate next)
{
_next = next ?? throw new ArgumentNullException(nameof(next));
}
public async Task Invoke(HttpContext httpContext)
{
if (httpContext == null) throw new
ArgumentNullException(nameof(httpContext));
// TODO - log the request
await _next(httpContext);
// TODO - log the response
}
}
This class contains a method called Invoke, which is the code that is executed in the request/response pipeline. The next method to call in the pipeline is passed into the class and held in the _next variable, which we need to invoke at the appropriate point in our Invoke method. The preceding example is a skeleton class for a custom logger. We would log the request details at the start of the Invoke method and log the response details after the _next delegate has been executed, which will be when the rest of the pipeline has been executed.
The following diagram is a visualization of the request/response pipeline and shows how each piece of middleware in the pipeline is invoked:
Figure 1.7: Visualization of the request / response pipeline
We make our middleware available as an extension method on the IApplicationBuilder interface in a new source file:
public static class MiddlewareExtensions
{
public static IApplicationBuilder UseCustomLogger(this
IApplicationBuilder app)
{
return app.UseMiddleware();
}
}
The UseMiddleware method in IApplicationBuilder is used to register the middleware class. The middleware will now be available in an instance of IApplicationBuilder in a method called UseCustomLogger.
So, the middleware can be added to the pipeline in the Configure method in the Startup class, as follows:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseCustomLogger();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseMvc(...);
app.UseSpa(...);
}
In the previous example, the custom logger is invoked at the start of the pipeline so that the request is logged before it is handled by any other middleware. The response that is logged in our middleware will have been handled by all the other middleware as well.
So, the Startup class allows us to configure how all requests are generally handled. How can we specify exactly what happens when requests are made to a specific resource in a web API? Let's find out.
Understanding controllers
Web API resources are implemented using controllers. Let's have a look at the controller the template project created by opening WeatherForecastController.cs in the Controllers folder. This contains a class called WeatherForecastController that inherits from ControllerBase with a Route annotation:
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
...
}
The annotation specifies the web API resource URL that the controller handles. The [controller] object is a placeholder for the controller name, minus the word Controller. This controller will handle requests to weatherforecast.
The method called Get in the class is called an action method. Action methods handle specific requests to the resource for a specific HTTP method and subpath. We decorate the method with an attribute to specify the HTTP method and subpath the method handles. In our example, we are handling an HTTP GET request to the root path (weatherforecast) on the resource:
[HttpGet]
public IEnumerable Get()
{
...
}
Let's have a closer look at the web API at runtime by carrying out the following steps:
- Run the app in Visual Studio by pressing F5.
- When the app has opened in our browser, press F12 to open the browser developer tools and select the Network panel.
- Select the Fetch data option on the top navigation bar. An HTTP GET request to weatherforecast will be shown:
Figure 1.8: Request to weatherforecast endpoint in the browser developer tools
- An HTTP response with a 200 status code is returned with JSON content: Figure 1.9: Response body for weatherforecast endpoint in the browser developer tools
If we look back at the Get action method, we are returning an object of the IEnumerable type. The MVC middleware automatically converts this object into JSON and puts it in the response body with a 200 status code for us.
So, that was a quick look at the backend that the template scaffolded for us. Now let’s walk through the React frontend.
Understanding the frontend
In this section, we'll discover where the single HTML page is, that hosts the React app. We'll also understand why it took over a minute to run the app for the first time.
Understanding the frontend entry point
We have a good clue as to where the entry point is from our examination of the Startup class in the ASP.NET Core backend. In the Configure method, the SPA middleware is set up with the source path as ClientApp:
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
If we look in the ClientApp folder, we'll see a file called package.json. This is a file that is often used in React apps and contains information about the project, its npm dependencies, and the scripts that can be run to perform tasks.
Important note
npm is a popular package manager for JavaScript. The dependencies in package.json reference packages in the npm registry.
If we open the package.json file, we will see react listed as a dependency:
"dependencies": {
"react": "^16.0.0",
...
"react-scripts": "^3.4.1",
...
},
A version is specified against each package name. The versions in your package.json may be different to the above code snippet. The ^ symbol in front of the version means that the latest minor version can be safely installed, according to semantic versioning.
Important Note
A semantic version has three parts, Major.Minor.Patch. A major version increment happens when an API breaking change is made. A minor version increment happens when backwards compatible features are added. A patch version happens when backwards compatible bug fixes are added. More information can be found at https://semver.org.
So, react 16.14.0 could be safely installed, because this is the latest minor version of React 16 at the time of writing.
The react-scripts dependency gives us a big clue as to how exactly React was scaffolded. react-scripts is a set of scripts from the popular Create React App (CRA) tool that was built by the developers at Facebook. This tool has done a huge amount of configuration for us, including a development server, bundling, linting, and unit testing.
The root HTML page for an app scaffolded by CRA is index.html, which can be found in the public folder in the ClientApp folder. It is this page that hosts the React app. The root JavaScript file that is executed for an app scaffolded by CRA is index.js, which is in the ClientApp folder. We'll examine both the index.html and index.js files later.
Running in development mode
In the following steps, we'll examine the ASP.NET Core project file to see what happens when the app runs in development mode:
- We can open the project file by right-clicking on the web application project in Solution Explorer and selecting the Edit Project File option: Figure 1.10: Opening the project file in Visual Studio
This is an XML file that contains information about the Visual Studio project.
- Let's look at the Target element, which has a Name attribute of DebugEnsureNodeEnv: <!-- Ensure Node.js is installed --> This executes tasks when the ClientApp/node-modules folder doesn't exist and the Visual Studio project is run in debug mode, which is the mode that's used when we press F5.
- The first task that is run in the Target element is the execution of the following command via an Exec task: > node --version This command returns the version of Node that is installed. This may seem like an odd thing to do, but its purpose is to determine whether node is installed. If node is not installed, the command will error and be caught by the Error task, which informs the user that Node needs to the installed and where to install it from.
- The next task in the Target element uses a Message command, which outputs Restoring dependencies using 'npm'. This may take several minutes... to the Output window. We'll see this message when running the project for the first time:
Figure 1.11: Restoring npm dependencies message when running a project for the first time
- The final task that is carried out when the project is run in debug mode is another Exec task that executes the following npm command: > npm install This command downloads all the packages that are listed as dependencies in package.json into a folder called node_modules: Figure 1.12: node_modules folder We can see this in Solution Explorer if the Show All Files option is on. Notice that there are a lot more folders in node_modules than dependencies listed in package.json. This is because the dependencies will have dependencies. So, the packages in node_modules are all the dependencies in the dependency tree. At the start of this section, we asked ourselves the question: Why did it take such a long time for the project to run the app for the first time? The answer is that this last task takes a while because there are a lot of dependencies to download and install. On subsequent runs, node_modules will have been created, so these sets of tasks won't get invoked. Earlier we learned that ASP.NET Core invokes an npm start command when the app is in development mode. If we look at the scripts section in package.json, we'll see the definition of this command: "scripts": { "start": "rimraf ./build && react-scripts start", ... } This command deletes a folder called build and runs a Webpack development server. Important note Webpack is a tool that transforms, bundles, and packages up files for use in a browser. Webpack also has a development server. The CRA tool has configured Webpack for us so that all the transformation and the bundling configuration are already set up for us. Why would we want to use the Webpack development server when we already have our ASP.NET Core backend running in IIS Express? The answer is a shortened feedback loop, which will increase our productivity. Later, we'll see that we can make a change to a React app running in the Webpack development server and that those changes are automatically loaded. There’s no stopping and restarting the application, making for a really quick feedback loop and great productivity.
Publishing process
The publishing process is the process of building artifacts to run an application in a production environment.
Let's carry on and inspect the XML ASP.NET Core project file and look at the Target element, which has the following Name attribute: PublishRunWebPack. The following code executes a set of tasks when the Visual Studio project is published:
<!-- As part of publishing, ensure the JS resources are freshly built
in production mode -->
<!-- Include the newly-built files in the publish output -->
Figure 1.13: Publishing to a folder
Enter a folder to output the content to and click Finish.
Figure 1.14: Publish locationA publish profile is then created. Click the Publish button to start the publishing process on the screen that appears.
Figure 1.15: Publish profile screen
After a while, we'll see the content appear in the folder we specified, including a ClientApp folder. If we look in this ClientApp folder, we'll see a build folder containing the React app, ready to be run in a production environment. Notice that the build folder contains index.html, which is the single page that will host the React app in production.
Important note
It is important to note that publishing from a developer's machine is not ideal. Instead, it is good practice to carry out this process on a build server to make sure that built applications are consistent, and code committed to the repository goes into the build.
Understanding the frontend dependencies
Earlier, we learned that frontend dependencies are defined in package.json. Why not just list all the dependencies as script tags in index.html? Why do we need the extra complexity of npm package management in our project? The answer is that a long list of dependencies is hard to manage. If we used script tags, we'd need to make sure these are ordered correctly. We'd also be responsible for downloading the packages, placing them locally in our project, and keeping them up to date. We have a huge list of dependencies in our scaffolded project already, without starting work on any functionality in our app. For these reasons, managing dependencies with npm has become an industry standard.
Let's open package.json again and look at the dependencies section:
"dependencies": {
"bootstrap": "^4.1.3",
"jquery": "3.4.1",
"merge": "^1.2.1",
"oidc-client": "^1.9.0",
"react": "^16.0.0",
"react-dom": "^16.0.0",
"react-router-bootstrap": "^0.24.4",
"react-router-dom": "^4.2.2",
"react-scripts": "^3.0.1",
"reactstrap": "^6.3.0",
"rimraf": "^2.6.2"
},
We've already observed the react dependency, but what is the react-dom dependency? Well, React doesn't just target the web; it also targets native mobile apps. This means that react is the core React library that is used for both web and mobile, and react-dom is the library that's specified for targeting the web.
The react-router-dom package is the npm package for React Router and helps us to manage the different pages in our app in the React frontend without a round-trip to the server. The react-router-bootstrap package allows Bootstrap to work nicely with React Router.
We can see that this React app has a dependency for Bootstrap 4.1 with the bootstrap npm package. So, Bootstrap CSS classes and components can be referenced to build the frontend in our project. The reactstrap package is an additional package that allows us to consume Bootstrap nicely in React apps. Bootstrap 4.1 has a dependency on jQuery, which is the reason for the jquery package dependency.
The merge package contains a function that merges objects together and oidc-client is a package for interacting with OpenID Connect (OIDC) and OAuth2.
The final dependency that we haven't covered yet is rimraf. This simply allows files to be deleted, regardless of the host operating system. We can see that this is referenced in the start script:
"scripts": {
"start": "rimraf ./build && react-scripts start",
...
}
Earlier we learned that this script is invoked when our app is running in development mode. So, rimraf ./build deletes the build folder and its contents before the development server starts.
If we look further down, we'll see a section called devDependencies. These are dependencies that are only used during development and not in production:
"devDependencies": {
"ajv": "^6.9.1",
"cross-env": "^5.2.0",
"eslint": "^6.8.0",
"eslint-config-react-app": "^5.2.1",
"eslint-plugin-flowtype": "^4.6.0",
"eslint-plugin-import": "^2.20.0",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-react": "^7.18.3"
},
The following is a brief description of these dependencies:
• ajv allows us to validate JSON files.
• cross-env allows us to set environment variables, regardless of the host operating system. If you look at the test script in the scripts section of the package.json file, it uses cross-env to set a CI environment variable.
• The remaining dependencies are all designed to enable linting with ESLint. The linting process checks for problematic patterns in code according to a set of rules.
Let's move on and learn how the single page is served and how the React app is injected into it.
Understanding how the single page is served
We know that the single page that hosts the React app is index.html, so let's examine this file. This file is found in the public folder in the ClientApp folder. The React app will be injected into the div tag that has an id of root:
Let's run our app again in Visual Studio to confirm that this is the case by pressing F5. If we open the developer tools in the browser page that opens and inspect the DOM in the Elements panel, we'll see this div with the React content inside it:
Figure 1.16: Root div element and script elements
Notice the script elements at the bottom of the body element. This contains all the JavaScript code for our React app, including the React library itself. However, these script elements don’t exist in the source index.html file, so how did it get there in the served page? Webpack added them after bundling all the JavaScript together and splitting it up into optimal chunks that can be loaded on demand. If we look in the ClientApp folder and subfolders, the static folder doesn't exist. The JavaScript files don’t exist either. What's going on? These are virtual files that are created by the Webpack development server. Remember that when we run the app with Visual Studio debugger the Webpack development server serves index.html. So the JavaScript files that the Webpack development server creates are virtual.
Now, what happens in production mode when the Webpack development server isn't running? Let's have a closer look at the app we published earlier. Let's look in the index.html file in the Build folder in the ClientApp folder. The script elements at the bottom of the body element will look something like the following:
!function(e){...}([])
Carriage returns have been added in the above code snippet to make it more readable. The highlighted part of the filenames may vary each time the app is published. The filenames are unique in order to break browser caching. If we look for these JavaScript files in our project, we'll find that these do exist. So, in production mode, the web server will serve this physical JavaScript file.
If we open this JavaScript file, it contains all the JavaScript for our app. The JavaScript is minified so that the file can be downloaded to the browser nice and quick.
Important note
Minification is the process of removing unnecessary characters in files without affecting how it is processed by the browser. This includes code comments and formatting, unused code, using shorter variable and function names, and so on.
However, the file isn't small and contains a lot of JavaScript. What's going on here? Well, the file contains not only our JavaScript app code but also the code from all the dependencies, including React itself.
Understanding how components fit together
Now it's time to start looking at the React app code and how components are implemented. Remember that the root JavaScript file is index.js in the ClientApp folder. Let's open this file and look closely at the following block of code:
const rootElement = document.getElementById('root');
ReactDOM.render(
,
rootElement);
The first statement selects the div element we discovered earlier, which has the root ID and stores it in a variable called rootElement.
The next statement extends over multiple lines and calls the render function from the React DOM library. It is this function that injects the React app content into the root div element. The rootElement variable, which contains a reference to the root div element, is passed into this function as the second parameter.
The first parameter that is passed into the render function is more interesting. In fact, it doesn't even look like legal JavaScript! This is, in fact, JSX.
Important note
JSX is transformed into regular JavaScript by Webpack using a tool called Babel. This is one of many tasks that CRA configured for us when our app was scaffolded.
So, the first parameter passes in the root React component called BrowserRouter, which comes from the React Router library.
Nested inside the BrowserRouter component is a component called App. If we look at the top of the index.js file, we can see that the App component is imported from a file called App.js:
import App from './App';
Important note
import statements are used to import items that are exported by another JavaScript module. The module is specified by its file location, with the js extension omitted.
The import statements that import items from npm packages don't need the path to be specified because CRA has configured a resolver in Webpack to automatically look in the node_modules folder during the bundling process.
So, the App component is contained in the App.js file. Let's have a look at this file. A class called App is defined in this file:
export default class App extends Component {
static displayName = App.name;
render () {
return (
);
}
}
Notice the export and default keywords before the class keyword.
Important note
The export keyword is used to export an item from a JavaScript module. The default keyword defines the export as the default export, which means it can be imported without curly braces. So, a default export can be imported as import App from './App' rather than import {App} from './App'.
A method called render defines the output of the component. This method returns JSX, which in this case references a Layout component in our app code and a Route component from React Router.
So, we are starting to understand how React components can be composed together to form a UI.
Let's now start to go through the React development experience by making a simple change:
- Run the app in Visual Studio by pressing F5 if it's not already running.
- Open up the Home.js file, which can be found at ClientApp\src\components. This contains the component that renders the home page.
-
With the app still running, in the render method, change the h1 tag in the JSX to render a different string:
render () {
return (Hello, React!
Welcome to your new single-page application, built with:
...);
} Save the file and look at the running app:
Figure 1.17: Home page is automatically updated in the browser
The app is automatically updated with our change. The Webpack development server automatically updated the running app with the change when the file was saved. The experience of seeing our changes implemented almost immediately gives us a really productive experience when developing our React frontend.
Understanding how components access the backend web API
The final topic in this overview is how the React frontend consumes the backend web API. If the app isn't running, then run it by pressing F5 in Visual Studio. If we click on the Fetch data option in the top navigation bar in the app that opens in the browser, we'll see a page showing weather forecasts:
Figure 1.18: Weather forecast data
If we cast our minds back, to the Understanding controllers section, we looked at the ASP.NET Core controller that surfaced a web API that exposed this data at weatherforecast. So, this is a good opportunity to have a quick look at how a React app can call an ASP.NET Core web API.
The component that renders this page is in FetchData.js. Let's open this file and look at the constructor class:
constructor (props) {
super(props);
this.state = { forecasts: [], loading: true };
}
The constructor in a JavaScript class is a special method that automatically gets invoked when a class instance is created. So, it's a great place to initialize class-level variables.
The constructor initializes some component state which contains the weather forecast data and a flag to indicate whether the data is being fetched.
Let's have a look at the componentDidMount method:
componentDidMount() {
this.populateWeatherData();
}
This method gets invoked by React when the component is inserted into the tree and is the perfect place to load data. This method calls a populateWeatherData method:
async populateWeatherData() {
const response = await fetch('weatherforecast');
const data = await response.json();
this.setState({ forecasts: data, loading: false });
}
Notice the async keyword before the populateWeatherData function name. Notice also the await keywords within the function.
Important note
An await keyword is used to wait for an asynchronous function to complete. A function must be declared as asynchronous in order to use the await keyword within. This is done by placing an async keyword in front of the function name. This is very much like async and await in .NET.
We can see that a function called fetch is used within this method.
Important note
The fetch function is a native JavaScript function for interacting with web APIs. The fetch function supersedes XMLHttpRequest and works a lot nicer with JSON-based web APIs.
The parameter that's passed into the fetch function is the path to the web API resource: weatherforecast. A relative path can be used because the React app and web API are in the same origin.
Once the weather forecast data has been fetched from the web API and the response has been parsed, the data is placed in the component's state.
Hang on a minute, though—the native fetch function isn't implemented in Internet Explorer (IE). Does that mean our app won't work in IE? Well, the fetch function isn't available in IE, but CRA has set up a polyfill for this so that it works perfectly fine.
Important note
A polyfill is a piece of code that implements a feature we expect the browser to provide natively. Polyfills allow us to develop against features that aren't supported in all browsers yet.
Let's now turn our attention to the render method:
render () {
let contents = this.state.loading
?
Loading...
: FetchData.renderForecastsTable(this.state.forecasts);
return (
Weather forecast
This component demonstrates fetching data from the server.
{contents}
);
}
The code may contain concepts you aren't yet familiar with, but don't worry if this doesn't make sense to you at this point.
We already know that the render method in a React component returns JSX, and we can see that JSX is returned in this render method as well. Notice the {contents} reference in the JSX, which injects the contents JavaScript variable into the markup below the p tag at the bottom of the div tag. The contents variable is set in the first statement in the render method and is set so that Loading... is displayed while the web API request is taking place along with the result of FetchData.renderForecastsTable when the request has finished, which we'll have a quick look at now:
static renderForecastsTable (forecasts) {
return (
Date | Temp. (C) | Temp. (F) | Summary |
---|---|---|---|
{forecast.dateFormatted} | {forecast.temperatureC} | {forecast.temperatureF} | {forecast.summary} |
);
}
This function returns JSX, which contains an HTML table with the data from the forecasts data array injected into it. The map method on the forecasts array is used to iterate through the items in the array and render tr tags in the HTML table containing the data.
Important note
The map method is a native JavaScript method that is available in an array. It takes in a function parameter that is called for each array element. The return values of the function calls then make up a new array. The map method is commonly used in JSX when iteration is needed.
Notice that we have applied a key attribute to each tr tag. What is this for? This isn't a standard attribute on an HTML table row, is it?
Important note
The key attribute helps React detect when an element changes, or is added or removed. So, it's not a standard HTML table row attribute. Where we output content in a loop, it is good practice to apply this attribute and set it to a unique value within the loop so that React can distinguish it from the other elements. Omitting keys can also lead to performance problems on large datasets as React will unnecessarily update the DOM when it doesn't need to.
Summary
We started off this overview by learning that all pages in an SPA are rendered in JavaScript with the help of a framework such as React, along with requests for data handled by a backend API with the help of a framework such as ASP.NET Core. We now understand that a class called Startup configures services that are used in the ASP.NET Core backend, as well as the request/response pipeline. Requests to specific backend API resources are handled by controller classes.
We also saw how CRA was leveraged by the ASP.NET Core React template to create the React app. This tool did a huge amount of setup and configuration for us, including a development server, bundling, linting, and even key polyfills for IE. We learned that the React app lives in the ClientApp folder in an ASP.NET Core React templated project, with a file called index.html being the single page. A file called package.json defines key project information for the React app, including its dependencies and the tasks that are used to run and build the React app.
Now you’ve seen how all the basic parts of an ASP.NET Core React app work together. Equipped with this knowledge, you are ready to embark on your own app-building adventure with React and ASP.NET Core.
Further reading
The following are some useful links so that you can learn more about the topics that were covered:
• ASP.NET Core startup: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/startup
• ASP.NET Core web API controllers: https://docs.microsoft.com/en-us/aspnet/core/web-api
• Create React app: https://facebook.github.io/create-react-app/
• WebPack development server: https://webpack.js.org/configuration/dev-server/
• npm: https://docs.npmjs.com/
• JSX: https://reactjs.org/docs/introducing-jsx.html
• JavaScript module import: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
• JavaScript module export: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export
• JavaScript fetch: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
• JavaScript array map: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
• React lists and keys: https://reactjs.org/docs/lists-and-keys.html
Purchase your copy on Amazon.com
Top comments (0)