Chapter 1: Understanding the ASP.NET 5 React Template

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 (https://github.com/facebook/react). 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 community and ecosystem. At the time of writing, React is downloaded over 8.8 million times per week, which is 2 million more than the same time a year ago. 

ASP.NET Core was first released in 2016 and is now a mature open source and cross-platform web application framework. It's 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 first chapter, we'll start by learning about the single-page application (SPA) architecture. Then, we'll create an ASP.NET Core and React app using the standard template in Visual Studio. We will use this to review and understand the critical parts of a React and ASP.NET Core app. Then, we'll learn where the entry points of both the ASP.NET Core and React apps are and how they integrate with each other. We'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 of this chapter, we'll have gained fundamental knowledge so that we can start building an app that uses both of these awesome technologies, something we'll gradually build upon throughout this book.

In this chapter, we'll cover the following topics:

  • SPA architecture
  • Understanding the ASP.NET Core backend
  • Understanding the React frontend

Let's get started!

Technical requirements

We will need to use the following tools in this chapter:

All the code snippets in this chapter can be found online at https://github.com/PacktPublishing/ASP.NET-Core-5-and-React-Second-Edition

Check out the following video to see the code in action: https://bit.ly/3riGWib.

SPA architecture

In this section, we will start to understand the single-page application (SPA) architecture.

A 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. When 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

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.

So, how are different pages with different URL paths handled? For example, if I enter https://qanda/questions/32139 in the browser's address bar, how does it go to the correct page in the app? Well, the browser's history API lets us change the browser's URL and handle changes in JavaScript. This process is often referred to as routing and, in Chapter 5Routing with React Router, we'll learn how we can build apps with different pages.

The SPA architecture is what we are going to use throughout this book. We'll use React to render our frontend and use ASP.NET Core for the backend API. 

Now that we have a basic understanding of the SPA architecture, we'll take a closer look at a SPA-templated app that Visual Studio can create for us.

Understanding the ASP.NET Core backend

In this section, 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.

Once we have scaffolded the app using the Visual Studio template, we will inspect the ASP.NET Core code, starting from its entry point. During our inspection, we will learn how the request/response pipeline is configured and how requests to endpoints are handled.

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:

  1. In the start-up dialog, choose Create a new project:
    Figure 1.2 – Visual Studio start-up dialog

    Figure 1.2 – Visual Studio start-up dialog

  2. Next, 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 Studio

    Figure 1.3 – Creating a new web app in Visual Studio

  3. Give the project a name of your choice and choose an appropriate location to save the project to. Then, click the Create button to create the project:
    Figure 1.4 – Specifying a project name and location

    Figure 1.4 – Specifying a 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.

  4. Select ASP.NET Core 5.0 as the version and React.js in the dialog. Then, click the Create button, which will create the project: 
    Figure 1.5 – The project template and ASP.NET Core version

    Figure 1.5 – The project template and ASP.NET Core version

    Important Note

    If ASP.NET Core 5.0 isn't listed, make sure that the latest version of Visual Studio is installed. This can be done by choosing the Check for Updates option from the Help menu.

  5. 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

Figure 1.6 – The home page of the app

We'll find out later in this chapter why the app took so long to run the first time. For now, 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<Startup>();

      });

}

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<Startup>();

    });

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. In this subsection, we will understand the two main methods within this class.

The ConfigureServices method

Services are configured using a method called ConfigureServices. This method is used to register items such as the following:

  • Our controllers, which will handle requests
  • Our authorization policies
  • Our CORS policies
  • Our own classes, which 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 since 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. We will use this method 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 in the 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 run for the first time. 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 later in this chapter. So, in development mode, the React app will be run on a development server rather than having ASP.NET Core serve the files from ClientApp/Build. We'll learn more about this development server later in this chapter.

Next, we will learn how custom middleware can be added to the ASP.NET Core request/response pipeline.

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

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<CustomLogger>();

    }

}

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 that 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 Get method 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<WeatherForecast> Get()

{

  ...

}

Let's have a closer look at the web API at runtime by carrying out the following steps:

  1. Run the app in Visual Studio by pressing F5.
  2. When the app has opened in our browser, press F12 to open the browser developer tools and select the Network panel.
  3. Select the Fetch data option from the top navigation bar. An HTTP GET request to weatherforecast will be shown:
    Figure 1.8 – A request to the weatherforecast endpoint in the browser developer tools

    Figure 1.8 – A request to the weatherforecast endpoint in the browser developer tools

  4. An HTTP response with a 200 status code is returned with JSON content:
Figure 1.9 – The response body for the weatherforecast endpoint in the browser developer tools

Figure 1.9 – The response body for the weatherforecast endpoint in the browser developer tools

If we look back at the Get action method, we are returning an object of the IEnumerable<WeatherForecast> 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. The request/response pipeline is configured in the Startup class and the endpoint handlers are implement using controller classes.

In the next section, we'll walk through the React frontend.

Understanding the React frontend

It's time to turn our attention to the React frontend. In this section, we'll inspect the frontend code, starting with the entry point, which is a single HTML page. We will explore how the frontend is executed in development mode and how it is built in preparation for deployment. We will then learn how the frontend dependencies are managed and also understand why it took over a minute to run the app for the first time. Finally, we will explore how React components fit together and how they access the ASP.NET Core backend.

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 set to 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 the 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 file may be different to the ones shown in the preceding 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 backward-compatible features are added. Finally, a patch version happens when backward-compatible bug fixes are added. More information can be found at https://semver.org.

So, react 16.14.0 can be safely installed because this is the latest minor version of React 16 at the time of writing this book.

The react-scripts dependency gives us a big clue as to how 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 creating a development server, bundling, linting, and unit testing. We'll learn more about CRA in the next chapter.

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 in this chapter. 

Next, we will learn how the React frontend is executed in development mode.

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:

  1. 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

    Figure 1.10 – Opening the project file in Visual Studio

    This is an XML file that contains information about the Visual Studio project.

  2. Let's look at the Target element, which has a Name attribute of DebugEnsureNodeEnv:

    <Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(SpaRoot)node_modules') ">

      <!-- Ensure Node.js is installed -->

      <Exec Command="node --version"

        ContinueOnError="true">

        <Output TaskParameter="ExitCode"

          PropertyName="ErrorCode" />

      </Exec>

      <Error Condition="'$(ErrorCode)' != '0'"

       Text="Node.js is required to build and run this

        project. To continue, please install Node.js from

         https://nodejs.org/, and then restart your

           command prompt or IDE."

       />

      <Message Importance="high" Text="Restoring

       dependencies using 'npm'.

        This may take several minutes..." />

      <Exec WorkingDirectory="$(SpaRoot)" Command="npm

        install" />

    </Target>

    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.

  3. 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.

  4. 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 we run the project for the first time:
    Figure 1.11 – Restoring npm dependencies message when running a project for the first time

    Figure 1.11 – Restoring npm dependencies message when running a project for the first time

  5. The final task that is carried out when the project is run in debug mode is another Exec task. This 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 – The node_modules folder

Figure 1.12 – The node_modules folder

We can see this in the Solution Explorer window 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 why it took 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 in this chapter, 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 bundling configuration is 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 is no stopping and restarting the application, so there's 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 continue and inspect the XML ASP.NET Core project file by looking at the Target element, which has a Name attribute of PublishRunWebPack. The following code executes a set of tasks when the Visual Studio project is published:

<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">

  <!-- As part of publishing, ensure the JS resources are

  freshly built in production mode -->

   

  <Exec WorkingDirectory="$(SpaRoot)" Command="npm install"

   />

  <Exec WorkingDirectory="$(SpaRoot)" Command="npm run

   build" />

  <!-- Include the newly-built files in the publish output -->

  <ItemGroup>

    <DistFiles Include="$(SpaRoot)build**" />

    <ResolvedFileToPublish Include="@(DistFiles-

     >'%(FullPath)')"

     Exclude="@(ResolvedFileToPublish)">

      <RelativePath>%(DistFiles.Identity)</RelativePath>

      <CopyToPublishDirectory>PreserveNewest

        </CopyToPublishDirectory>

    </ResolvedFileToPublish>

  </ItemGroup>

</Target>

The first task that is run is the execution of the npm install command via an Exec task. This will ensure that all the dependencies are downloaded and installed. Obviously, if we've already run our project in debug mode, then the dependencies should already be in place.

The next task is an Exec task that runs the following npm command:

> npm run build

This task will run an npm script called build. If we look in the package.json file again, we'll see this script in the scripts section: 

"scripts": {

  "start": "rimraf ./build && react-scripts start",

  "build": "react-scripts build",

  "test": "cross-env CI=true react-scripts test --

   env=jsdom",

  "eject": "react-scripts eject",

  "lint": "eslint ./src/"

}

This references the create-react-app scripts, which bundle the React app ready for production, optimizing it for great performance, and outputting the content into a folder called build

The next set of tasks defined in the ItemGroup element take their content from the build folder and place it in the publish location, along with the rest of the content to publish.

Let's give this a try and publish our app:

  1. In the Solution Explorer window, right-click on the project and select the Publish... option.
  2. Choose Folder as the target and click Next:
    Figure 1.13 – Publishing to a folder

    Figure 1.13 – Publishing to a folder

  3. Enter a folder location to output the content to and click Finish:
    Figure 1.14 – Publish location

    Figure 1.14 – Publish location

  4. A 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

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 that code that's committed to the repository goes into the build. We'll cover this in Chapter 15Implementing CI and CD with Azure DevOps.

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 manage the different pages in our app in the React frontend, without us needing to do a round trip to the server. We'll learn more about React Router in Chapter 5Routing with React Router. 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 why we have the jquery package dependency. 

The merge package contains a function that merges objects together, while 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 in this chapter, 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, you'll see that 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. We'll learn more about ESLint in Chapter 3Getting Started with React and TypeScript.

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 can be found in the public folder of the ClientApp folder. The React app will be injected into the div tag, which has an id of root:

<div id="root"></div>

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 tag with the React content inside it:

Figure 1.16 – Root div element and script elements

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 they 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, we'll see that 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 are virtual files that the Webpack development server creates.

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 in this chapter. Let's look in the index.html file in the Build folder, which can be found in the ClientApp folder. The script elements at the bottom of the body element will look something like the following: 

<script>

  !function(e){...}([])

</script>

<script src="/static/js/2.f6873cc5.chunk.js"></script>

<script src="/static/js/main.61537c83.chunk.js"></script>

Carriage returns have been added in the preceding code snippet to make it more readable. The highlighted parts 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 they do exist. So, in production mode, the web server will serve this physical JavaScript file.

If we open this JavaScript file, we'll see it contains all the JavaScript for our app. This JavaScript is minified so that the file can be downloaded to the browser nice and quickly. 

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(

  <BrowserRouter basename={baseUrl}>

    <App />

  </BrowserRouter>,

  rootElement);

The first statement selects the div element we discovered earlier, which contains 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, which we'll learn about in detail in Chapter 3Getting Started with React and TypeScript.

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. We'll learn more about this component in Chapter 5Routing with React Router.

Nested inside the BrowserRouter component is a component called App. If we look at the top of the index.js file, we will 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 have been 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. This is because CRA has configured a resolver in Webpack that automatically looks in the node_modules folder during the bundling process.

So, the App component is contained in the App.js file. Let's have a quick look. A class called App is defined in this file: 

export default class App extends Component {

  static displayName = App.name;

  render () {

    return (

      <Layout>

        <Route exact path='/' component={Home} />

        <Route path='/counter' component={Counter} />

        <Route path='/fetch-data' component={FetchData} />

      </Layout>

    );

  }

}

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.

Now, let's go through the React development experience by making a simple change:

  1. Run the app in Visual Studio by pressing F5, if it's not already running.
  2. Open the Home.js file, which can be found at ClientAppsrccomponents. This contains the component that renders the home page.
  3. With the app still running, in the render method, change the h1 tag in the JSX so that it renders a different string:

    render () {

      return (

        <div>

          <h1>Hello, React!</h1>

          <p>Welcome to your new single-page application,

             built with:

          </p>

          ...

        </div>

      );

    }

  4. Save the file and look at the running app:
Figure 1.17 – The home page is automatically updated in the browser

Figure 1.17 – The 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 we'll cover in this chapter 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

Figure 1.18 – Weather forecast data

If we cast our minds back to earlier in this chapter, in the Understanding controllers section, we looked at an ASP.NET Core controller that surfaced a web API that exposed the data at weatherforecast. So, this is a great place 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 class 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 a component state, which contains the weather forecast data, and a flag to indicate whether the data is being fetched. We'll learn more about component state in Chapter 3Getting Started with React and TypeScript.

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, so, let's have a look at that:

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. Also, notice 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 for us to use the await keyword within it. This can be done by placing the 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; that is, weatherforecast. A relative path can be used because the React app and web API are of 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

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.

Now, let's turn our attention to the render method:

render () {

  let contents = this.state.loading

    ? <p><em>Loading...</em></p>

    : FetchData.renderForecastsTable(this.state.forecasts);

  return (

    <div>

      <h1 id="tabelLabel">Weather forecast</h1>

      <p>This component demonstrates fetching data from the

        server.</p>

      {contents}

    </div>

  );

}

The code may contain concepts you aren't familiar with, so don't worry if this doesn't make sense to you at this point. I promise that it will make sense as we progress through this book!

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. We'll have a quick look at this now:

static renderForecastsTable (forecasts) {

  return (

    <table className='table table-striped' aria-

      labelledby="tabelLabel">

      <thead>

        <tr>

          <th>Date</th>

          <th>Temp. (C)</th>

          <th>Temp. (F)</th>

          <th>Summary</th>

        </tr>

      </thead>

      <tbody>

        {forecasts.map(forecast =>

          <tr key={forecast.dateFormatted}>

            <td>{forecast.dateFormatted}</td>

            <td>{forecast.temperatureC}</td>

            <td>{forecast.temperatureF}</td>

            <td>{forecast.summary}</td>

          </tr>

        )}

      </tbody>

    </table>

  );

}

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.

Again, this is a lot to take in at this point, so don't worry if there are bits you don't fully understand. This will all become second nature to you by the end of this book.

Summary

In this chapter, we started off by learning that all the pages in a SPA are rendered in JavaScript with the help of a framework such as React, along with requests for data. This is handled by a backend API with the help of a framework such as ASP.NET Core. We now understand that the Startup class 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 creating a development server, bundling, linting, and even creating 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.

This chapter has given us a great overview of all the basic parts of an ASP.NET Core React app and how they work together. We'll explore many of the topics we've covered in this chapter in greater depth throughout this book.

With the knowledge we've gained from this chapter, we are now ready to start creating the app we are going to build through this book, which we'll start to do in the next chapter.

Questions

Have a go at answering the following questions to test the knowledge that you have acquired in this chapter:

  1. What is the entry point method in an ASP.NET Core app?
  2. What is the single HTML page filename in an ASP.NET Core React app that's created by a template? What folder is this located in?
  3. What file are React app dependencies defined in?
  4. What npm command will run the React app in the Webpack development server?
  5. What npm command builds the React app so that it's ready for production?
  6. What is the method name in a React class component that renders the component?
  7. Have a look at the following code snippet, which configures the request/response pipeline in an ASP.NET Core app: 

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)

    {

      app.UseAuthentication();

      app.UseHttpsRedirection();

      app.UseMvc();

    }

    Which is invoked first in the request/response pipeline – authentication or the MVC controllers?

  8. Does the class that configures the services and request/response pipeline need to be called Startup? Can we give it a different name?
  9. What browsers are supported by a React app created by CRA?

Answers

  1. A method called Main in the Program class is the entry point method in an ASP.NET Core app.
  2. A file called index.html is the single HTML page filename. This is located in the public folder, which can be found in the ClientApp folder.
  3. The React app dependencies are defined in a file called package.json in the ClientApp folder.
  4. npm start is the command that will run the React app in the WebPack development server.
  5. npm run build is the command that builds the React app so that it's ready for production.
  6. The render method renders a React class component.
  7. Authentication will be invoked first in the request/response pipeline.
  8. We can give the Startup class a different name by defining this class in IHostBuilder, as shown in the following example:

    public static IHostBuilder CreateHostBuilder(string[] args) =>

      Host.CreateDefaultBuilder(args)

        .ConfigureWebHostDefaults(webBuilder =>

        {

            webBuilder.UseStartup<MyStartup>();

        });

  9. All modern browsers, including IE, are supported by a React app created by CRA.

Further reading

The following are some useful links so that you can learn more about the topics that were covered in this chapter:

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.226.169.94