In this article, we will build React websites that seamlessly work with ASP.NET Core 3.0 as the back-end server. By the end of the article, we will show you how to create a weather Client with Create-React-App, implementation using Form Components, styling UI with Bootstrap, and call the .NetCore API.
Introduction
I’ve introduced how to build Angular App with .NET Core in Global Weather – Angular 7 App With .NET Core series (Part 1, Part 2 and Part 3). Now let’s talk about another popular technology React. React, Angular have become incredibly popular because these single-page app frameworks building apps that run self-contained in a browser page is hugely powerful.
React vs. Angular
Angular and React have many similarities and many differences. Angular is an MVC framework and structures the application very well, but you have less flexibility. React only provides the “view” in MVC – you need to solve the M and C on your own. Due to this, you can choose any of your own libraries as you see fit.
Both React and Angular are component based. A component receives an input, and returns a rendered UI template as output.
React’s use of a virtual DOM is to make it so fast. A virtual DOM only looks at the differences between the previous and current HTML and changes the part that is required to be updated. Angular uses a regular DOM. This will update the entire tree structure of HTML tags until it reaches the user’s age.
React decided to combine UI templates and inline JavaScript/Typescript logic, which no company had ever done before. The result is called “JSX”(javascript) or “TSX” (typescript). JSX/TSX is a big advantage for development, because you have everything in one place, and code completion and compile-time checks work better.
React uses one-way binding, and Redux is often used as the solution to handle state. Angular uses two-way binding. One-way data binding means that React is more performant than Angular and also makes debugging a React app easier than debugging an Angular app.
Create-React-App
The best solution to build a React app is using create-react-app
. Create-React-App
is maintained by Facebook. Create React App sets up your development environment so that you can use the latest JavaScript/TypeScript features, provides a nice developer experience, and optimizes your app for production. You’ll need to have Node >= 8.10 and npm >= 5.6 on your machine.
To create a JavaScript project, run:
npx create-react-app my-app
cd my-app
npm start
To create a typescript project, run:
npx create-react-app my-app --typescript
cd my-app
npm start
Create ASP.NET Core Web Project from Visual Studio 2019
Open your Visual Studio 2019 -> Create New Project -> Select ASP.NET Core Web application.
Give the project name as “GlobalWeatherReact
”.
Click “Create”, and in the next window, select ASP.NET Core 3.0 and Empty as shown below:
Click “Create”, then a default ASP.NET Core project is created.
Leave it for the time being. After we create a React client, need back here to change some code.
Create Weather Client with Create-React-App
Once the API project is created, open the Powershell and navigate to the GlobalWeather project folder and run the following command:
npx create-react-app weatherclient --typescript
This will create a latest version of React application. Now the solution structure should be like this:
TSX is typescript of JSX. JSX is an embeddable XML-like syntax. It is meant to be transformed into valid JavaScript, though the semantics of that transformation are implementation-specific. JSX rose to popularity with the React framework. TypeScript supports embedding, type checking, and compiling JSX directly to JavaScript.
Now we need make some changes in ASP.NET Core project to make it smoothly integrate with React client.
First, install ASP.NET Core SPA Services and ASP.NET Core SPA Services Extensions in Nuget Manager.
Open Startup.cs, add SpaStaticFiles
in ConfigureServices
method.
services.AddMvc(option => option.EnableEndpointRouting = false);
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "weatherclient/build";
});
And add the below lines in the Configuration
method:
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseMvc();
app.UseSpa(spa =>
{
spa.Options.SourcePath = Path.Join(env.ContentRootPath, "weatherclient");
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
OK. Now just run it, click “IISExpress”.
Wow, the React client is starting with ASP.NET Core API project!
React Router and Component
The Hello World react app is a single-component app. But most real world apps are multi-component app. React Router is a collection of navigational components that compose declaratively with your application. In single page apps, there is only a single HTML page. We are reusing the same HTML page to render the different components based on the navigation.
React Component
React lets you define components as classes or functions. Components defined as classes currently provide more features. To define a React component class, you need to extend React.Component
.
Each component has several “lifecycle methods” that you can override to run code at particular times in process. The component life cycle is listed as below.
- Mounting
These methods are called in the following order when an instance of a component is being created and inserted into the DOM.
constructor()
static getDerivedStateFromProps()
render()
componentDidMount()
- Updating
An update can be caused by changes to props or state. These methods are called in the following order when a component is being re-rendered.
static getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()
- Unmounting
This method is called when a component is being removed from the DOM.
componentWillUnmount()
- Error Handling
These methods are called when there is an error during rendering, in a lifecycle method, or in the constructor of any child component.
static getDerivedStateFromError()
componentDidCatch()
First, we add a Home component first, move the code from App.tsx to Home component.
Create “components” folder under “weatherclient/src”. Then add Home.tsx.
Home component extends React.Component
. We copy render()
from App.tsx.
import React from 'react';
import logo from '../logo.svg';
class Home extends React.Component {
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.tsx</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
}
export default Home;
React-router-dom
React-router-dom
exports DOM-aware components, like <Link>
(which renders an <a>
) and <BrowserRouter>
(which interacts with the browser's window.history
).
React-router-dom
re-exports all of react-router
's exports, so you only need to import from react-router-dom
in your project.
Now we install react-router-dom
first. In Powershell, install react-router-dom
under “weatherclient” folder.
npm install @types/react-router-dom react-router-dom --save
Add Router to App
Create “router” folder under “weatherclient/src”, then add index.tsx.
import React from "react";
import { Router, Route, Switch, Link, NavLink } from "react-router-dom";
import * as createHistory from "history";
import Home from "../components/Home";
export const history = createHistory.createBrowserHistory();
const AppRouter = () => (
<Router history={history}>
<div>
<Switch>
<Route path="/" component={Home} />
</Switch>
</div>
</Router>
);
export default AppRouter;
Then we go back to change App.tsx.
import React from 'react';
import "./App.css";
import AppRouter from "./router";
const App: React.FC = () => {
return (
<AppRouter />
);
}
export default App;
Basically, it removes fall rendering code, and replace with AppRouter
.
Now, click “IIS Express” to run it again.
App Router is working. It automatically navigates to the Home component.
Function Component Vs. Class Component
You may notice App
Component is a function component and Home
Component is a class component.
But what’s the difference between function component and class component?
Functional components are basic JavaScript/TypeScript functions. These are typically arrow functions but can also be created with the regular function keyword. React lifecycle methods cannot be used in functional components. There is no render
method used in functional components. Functional components should be favored if you do not need to make use of React state.
Class components make use of ES6 class and extend the Component
class in React. React lifecycle methods can be used inside class components. Class components have internal state. “this.state
” read state and “this.setState
” updates state.
Implement WeatherDetails and Form Components
What We Want to Build?
After introducing the basic react stuff, now it’s the time to build our weather app. We’ll build a very similar app as my series articles Global Weather - Angular 7 App with .NET Core 2.2.
So basically, this app has two parts. One is a panel which shows weather details. The other is an input form where we can input country
and city
. Now let’s build the app step by step.
WeatherDetails Component
Add WeatherDetails.tsx under “components” folder with the below code:
import React from 'react';
class WeatherDetails extends React.Component {
render() {
return (
<div>
Weather Details
</div>
);
}
}
export default WeatherDetails
Add Form.tsx under “components” folder with the below code:
import React from 'react';
class Form extends React.Component {
render() {
return (
<div>
<input id="country" type="text" name="country" placeholder=" Country... " />
<input id="city" type="text" name="city" placeholder=" City... " />
</div>
);
}
}
export default Form
Then change Home
component to use WeatherDetails
and Form
.
import React from 'react';
import Form from './Form';
import WeatherDetails from './WeatherDetails';
class Home extends React.Component {
render() {
return (
<div className="App">
<WeatherDetails />
<Form />
</div>
);
}
}
export default Home;
Click “IISExpress
” to run it.
That’s it. Now we create types first, then back to components to do more work.
Types
We’re developing a website to display weather information via AccuWeather REST API. The user can select whatever location and show current weather information.
Create an account to obtain an API key to use against the APIs.
Users should be able to narrow their location search by country.
The API reference introduces all response details for the different API request. For our app, we only care about country, city and current weather.
So according to json response, we create the corresponding type
, Country
, City
and CurrentCondition
types.
Create “types” folder under “weatherclient/src”. Add Country.ts to “types” folder.
Then copy the below code to Country.ts.
export interface Country {
ID: string;
EnglishName: string;
};
Then add City
type in City.ts.
import { Country } from './Country';
export interface City {
Key: string;
EnglishName: string;
Type: string;
Country: Country;
};
Add CurrentCondition type
in CurrentCondition.ts.
export interface CurrentCondition {
LocalObservationDateTime: string;
WeatherText: string;
WeatherIcon: number;
IsDayTime: boolean;
Temperature: Temperature;
};
export interface Metric {
Unit: string;
UnitType: number;
Value: number;
};
export interface Imperial {
Unit: string;
UnitType: number;
Value: number;
};
export interface Temperature {
Imperial: Imperial;
Metric: Metric;
};
For WeatherDetails
component, I don’t want to bind CurrentCondition
directly, so create a view model class, Weather
.
Add Weather.ts to “types” folder.
import { CurrentCondition } from './CurrentCondition';
import { City } from './City'
import { Constants } from '../Constants';
export class Weather {
public location?: string;
public weatherIcon?: any;
public weatherText?: string;
public temperatureValue?: number;
public temperatureUnit?: string;
public isDaytime?: boolean;
public error?: string;
public constructor(currentConditions: CurrentCondition, city: City) {
this.location = city.EnglishName!;
this.weatherText = currentConditions.WeatherText;
this.isDaytime = currentConditions.IsDayTime;
if (currentConditions.WeatherIcon) {
let icon = currentConditions.WeatherIcon.toString();
if (icon.length === 1)
icon = "0" + icon;
this.weatherIcon = `${Constants.weatherIconUrl}${icon}-s.png`;
}
this.temperatureValue = currentConditions.Temperature.Metric.Value;
this.temperatureUnit = currentConditions.Temperature.Metric.Unit;
}
}
Here, you can see we make up weather icon as an HTML link URL. You can find all weather icons from Accure Weather Icons.
Styling UI With Bootstrap, React-Bootstrap and React-Bootstrap-Typeahead
Bootstrap is an open source toolkit for developing with HTML, CSS, and JS. Quickly prototype your ideas or build your entire app with our Sass variables and mixins, responsive grid system, extensive prebuilt components, and powerful plugins built on jQuery.
React-Bootstrap replaces the Bootstrap JavaScript. Each component has been built from scratch as a true React component, without unneeded dependencies like jQuery.React-Bootstrap
provides a bunch of components like Button
, Form
, Accordion
, Badge
, Alter
, Dropdown
.
React-Bootstrap-Typeahead
is a React-based typeahead
that relies on Bootstrap for styling. It supports both single- and multi-selection.
We use bootstrap for styling and React-Boostrap
input and button components. Also, we use React-Bootstrap-Typeahead
for Country
input.
Install Bootstrap
, React-Bootstrap
and React-Bootstrap-Typeahead
under “weatherclient” folder.
npm install react-bootstrap bootstrap –-save
npm install react-bootstrap-typeahead @types/react-bootstrap-typeahead --save
After installing bootstrap
, go to index.tsx under src folder to add the below line:
import 'bootstrap/dist/css/bootstrap.min.css'
This line apples bootstrap
styling to the whole app.
Now we can style our app, replace App.css with the following styles:
.panel {
padding-left: 0px;
padding-top: 10px
}
.field {
padding-left: 10px;
padding-right: 10px
}
.city {
display: flex;
background: linear-gradient(90deg, rgba(2,0,36,1) 0%,
rgba(25,112,245,0.6399510487788865) 0%, rgba(0,212,255,1) 100%);
flex-direction: column;
height: 40vh;
justify-content: center;
align-items: center;
padding: 0px 20px 20px 20px;
margin: 0px 0px 50px 0px;
border: 1px solid;
border-radius: 5px;
box-shadow: 2px 2px #888888;
font-family: 'Merriweather', serif;
}
.city h1 {
line-height: 1.2
}
.city span {
padding-left: 20px
}
.city .row {
padding-top: 20px
}
.weatherError {
color: #f16051;
font-size: 20px;
letter-spacing: 1px;
font-weight: 200;
}
Rework Components with IState and IProp
Form Component
We change Form
component to a state component. React
class component has a built-in state object. The state
object is where you store property values that belong to the component. When the state object changes, the component re-renders.
Now we add state and props to our form component.
import React from "react";
import { Button, FormControl } from 'react-bootstrap';
import { AsyncTypeahead, Typeahead } from 'react-bootstrap-typeahead';
import { Country } from '../types/Country';
import { City } from '../types/City';
interface IState {
city: City;
country: Country;
cities: City[];
searchText: string
};
interface IProps {
countries: Country[];
}
class Form extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
this.state = {
city: {} as City,
country: {} as Country,
cities: [],
searchText: ""
}
};
handleSubmit = async (e: any) => {
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<div className="container-fluid">
<div className="row">
<div className="col-sm-4 form-group">
<Typeahead
id="country"
labelKey="EnglishName"
options={this.props.countries}
onChange={(s) => this.setState({ country: s[0] } as IState)}
placeholder="Country..."
/>
</div>
<div className="col-sm-4 form-group field">
<FormControl id="city" type="text" name="city"
onChange={(e: any) => this.setState
({ searchText: e.target.value })} placeholder=" City... " />
</div>
<div className="col-sm-2 form-group field">
<Button variant="primary" type="submit"> Go </Button>
</div>
</div>
</div>
</form>
);
}
};
export default Form;
Here, we use react-bootstrap-typeahead
for country
input, and use react-boostrap FormControl
for city
text input, also use react-bootstrap
Button for “Go” button. The one thing I think I need mention is OnChange
event of input fields.
As I mentioned before, React is one way binding, which means when you change the UI field value, binding data model is not get updated automatically as Angular. That’s why we need update the binding data model via onChange
event. Also react doesn’t allow you to change component state directly, you need to call setState
method to update state.
We leave handleSubmit
as empty function now, and will come back later.
WeatherDetails Component
WeatherDetails
component is pretty straight forward, just binding Weather
view model and display.
import React from 'react';
import { Weather } from '../types/Weather'
interface IProp {
weather: Weather,
};
class WeatherDetails extends React.Component<IProp> {
render() {
const weather = this.props.weather;
return (
<div>
<div className="city col-sm-9">
{
weather.location && <div>
<h1>{weather.location}</h1>
<div className="row">
<table>
<tbody>
<tr>
<td>
{
weather.weatherIcon &&
<img src={weather.weatherIcon}
className="img-thumbnail" />
}
</td>
<td>
<span>{weather.weatherText}</span>
</td>
</tr>
<tr>
<td>
{weather.isDaytime && <span>
Daytime
</span>}
{!weather.isDaytime && <span>
Night
</span>}
</td>
<td>
<span>{weather.temperatureValue}°
{weather.temperatureUnit}</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
}
</div>
{
weather.error && <p className="weatherError">
{weather.error}
</p>
}
</div>
);
}
};
export default WeatherDetails;
Here, we pass “weather
” to WeatherDetails
component as prop. In render
function, binding prop
to HTML fields. Also you can see we’re using conditional rendering.
Conditional rendering in React works the same way conditions work in JavaScript. Use JavaScript operators like if
or the conditional operator to create elements representing the current state, and let React update the UI to match them. In a conditional render
, a component decides based on one or several conditions which elements it will return. When a component has conditional rendering, the instance of the rendered component can have different looks.
Home Component
First, define state interface for Home
component.
interface IState {
weather: Weather,
countries: Country[],
city?: City
}
Then initialize the state
in constructor of component.
public state: IState = {
weather: {
error: ""
} as Weather,
countries: [],
city: undefined
}
In render
function, pass state
values to WeatherDetails
and Form
components.
render() {
return (
<div className="container content panel">
<div className="container">
<div className="row">
<div className="form-container">
<WeatherDetails weather={this.state.weather} />
<Form countries={this.state.countries} />
</div>
</div>
</div>
</div>
);
}
Now click “IIS Express” to run again.
How to Consume Restful API In React
Most modern web applications make use of the REST Protocol to communicate with each other. To achieve this, data is sent as JSON (JavaScript Object Notation) to the API. In turn, the API returns a JSON payload which can be static or dynamic data.
The Fetch
API provides an interface for fetching resources (including across the network). It will seem familiar to anyone who has used XMLHttpRequest
, but the new API provides a more powerful and flexible feature set. Fetch
API is builtin package of Create-React-App
. So we don’t need install anything, just use it directly.
Add Constants.ts, put the API URLs and API key in Constants
class.
export class Constants {
static locationAPIUrl = 'https://dataservice.accuweather.com/locations/v1';
static citySearchAPIUrl = 'https://dataservice.accuweather.com/locations/v1/cities/';
static currentConditionsAPIUrl = 'https://dataservice.accuweather.com/currentconditions/v1';
static weatherIconUrl = 'http://developer.accuweather.com/sites/default/files/';
static apiKey = 'XXXXXXXXXXXXXXXXXX';
}
You should replace apiKey
with your API key.
Get All Countries
Call Country List
endpoint of Accure Weather Locations API.
Resource URL,
- https://dataservice.accuweather.com/locations/v1/countries
Add the below function to Home
component.
async getCountries(): Promise<Country[]> {
try {
const res = await fetch
(`${Constants.locationAPIUrl}/countries?apikey=${Constants.apiKey}`);
return await res.json() as Country[];
} catch (error) {
console.log(error);
return [];
}
}
Here, we call http get by “fetch
”, and json deserialize the result to array of Country
. In short, the Promise
object represents the eventual completion (or failure) of an asynchronous operation, and its resulting value.
Now we have getCountries
function, but where do we need to call it? We talked react component life cycle before, we need call getCountries()
when Home
component is being created and inserted into the DOM. Then the best location is componentDidMount()
.
async componentDidMount() {
try {
const countries = await this.getCountries();
await this.setStateAsync({ countries: countries } as IState);
} catch (error) {
}
}
Add setStateAsync
as well.
async setStateAsync(state: IState) {
return new Promise((resolve: any) => {
this.setState(state, resolve);
});
}
Why do we use async await
?
Async
and Await
are extensions of promises. Async
functions enable us to write promise based code as if it were synchronous, but without blocking the execution thread. The await
operator is used to wait for a Promise
.
OK. Now we run the app again.
Because we are binding “Country
” typeahead with “countries
” prop in Form
component, and passing countries from Home
component to Form
component, the typeahead shows options as expected.
Get City
After select a country
, we can search the city
per the input text.
City
search endpoint:
- http://dataservice.accuweather.com/locations/v1/cities/{countryCode}/search
Add getCity
function to Home
component.
async getCity(searchText: string, countryCode: string): Promise<City> {
const res = await fetch(`${Constants.locationAPIUrl}/cities/
${countryCode}/search?apikey=${Constants.apiKey}&q=${searchText}`);
const cities = await res.json() as City[];
if (cities.length > 0)
return cities[0];
return {} as City;
}
Get Current Conditions
GetCity
returns a City
type object, which has location
key. With location
key, we can call Current Conditions endpoint of Current Conditions API.
Current conditions endpoint:
- http://dataservice.accuweather.com/currentconditions/v1/{locationKey}
Add getCurrentConditions
function to Home
component.
async getCurrentConditions(city: City) {
try {
const res = await fetch(`${Constants.currentConditionsAPIUrl}/
${city.Key}?apikey=${Constants.apiKey}`);
const currentConditions = await res.json() as CurrentCondition[];
if (currentConditions.length > 0) {
const weather = new Weather(currentConditions[0], city);
await this.setStateAsync({
weather: weather,
city: city
} as IState);
}
} catch (error) {
console.log(error);
}
return {} as Weather;
}
Now we put getCity
and getCurrentConditions
together in getWeather
.
getWeather = async (e: any, countryCode: string, searchText: string) => {
e.preventDefault();
if (!countryCode && !searchText) {
await this.setStateAsync
({ weather: { error: "Please enter the value." } } as IState);
return;
}
try {
const city = await this.getCity(searchText, countryCode);
if (city.Key) {
await this.getCurrentConditions(city);
}
} catch (err) {
await this.setStateAsync({ weather: { error: err } } as IState);
}
};
It appears getWeather
has two parameters, countryCode
and searchText
. These values come from Form
component, which means getWeather
should be called from Form
component. How can we do that?
Just add getWeather
to Form
component prop, and set it from Home
component.
First, we add getWeather
to Form
prop. In Form.tsx, we change Iprops interface
.
interface IProps {
getWeather: (e: any, country: string, serachText: string) => Promise<void>;
countries: Country[];
}
Then change handleSubmit
:
handleSubmit = async (e: any) => {
this.props.getWeather(e, this.state.country.ID, this.state.searchText);
}
Don’t forget set getWeather
prop in Home
component.
<div className="form-container">
<WeatherDetails weather={this.state.weather} />
<Form getWeather={this.getWeather} countries={this.state.countries} />
</div>
Click “IISExpress” to run again.
Select “Australia”, and input “Knoxfield”, then click “Go”, the weather conditions displays as we expected.
Call .NetCore API
Now we move to backend .NetCore API controller. Our target is saving the last selected location to database. And when we load the react app, will retrieve the last input location to get the weather conditions automatically.
All backend code is the same as Global Weather – ASP.NetCore and Angular 7 (part 2). I don’t explain again. Just quick copy all the code. Run “Release 1.0.sql” to create the database.
Basically, we created Cities
API, one is http get, the other is http post.
using System.Threading.Tasks;
using GlobalWeather.Services;
using Microsoft.AspNetCore.Mvc;
using Serilog;
using Weather.Persistence.Models;
namespace GlobalWeather.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class CitiesController : ControllerBase
{
private readonly ICityService _service;
private readonly ILogger _logger;
public CitiesController(ICityService service, ILogger logger)
{
_service = service;
_logger = logger;
}
[HttpGet]
public async Task<ActionResult<City>> Get()
{
var city = await _service.GetLastAccessedCityAsync();
return city;
}
[HttpPost]
public async Task Post([FromBody] City city)
{
await _service.UpdateLastAccessedCityAsync(city);
}
}
}
Now we need call this API from React app.
First, add CityMetaData.ts to “types” folder.
import { City } from './City';
export class CityMetaData {
public id: string;
public name: string;
public countryId: string;
public constructor(city: City) {
this.id = city.Key;
this.name = city.EnglishName;
this.countryId = city.Country.ID;
}
}
Then add City API Url to Constants.ts:
static cityAPIUrl = '/api/cities';
Finally, add getLastAccessedCity
and updateLastAccessedCity
to Home
component.
async updateLastAccessedCity(city: City) {
try {
const data = new CityMetaData(city);
await fetch(`${Constants.cityAPIUrl}`, {
method: 'post',
body: JSON.stringify(data)
});
} catch (error) {
console.log(error);
}
}
async getLastAccessedCity(): Promise<City> {
try {
const res = await fetch(`${Constants.cityAPIUrl}`);
const data = await res.json() as CityMetaData;
return {
Key: data.id,
EnglishName: data.name,
Type: 'City',
Country: {
ID: data.countryId,
EnglishName: ''
}
} as City;
} catch (error) {
console.log(error);
return {} as City;
}
}
In componentDidMount
function to call getLastAccessedCity
to retrieve last accessed city, if there is a last accessed city, just get weather of this city
directly.
async componentDidMount() {
try {
const countries = await this.getCountries();
await this.setStateAsync({ countries: countries } as IState);
const lastCity = await this.getLastAccessedCity();
if (lastCity && lastCity.Key) {
await this.getWeatherAsync(lastCity);
}
} catch (error) {
}
}
Change getWeather
function to call updateLastAccessedCity
to save the current city.
getWeather = async (e: any, countryCode: string, searchText: string) => {
e.preventDefault();
if (!countryCode && !searchText) {
await this.setStateAsync({ weather: { error: "Please enter the value." } } as IState);
return;
}
try {
const city = await this.getCities(searchText, countryCode);
if (city.Key) {
await this.updateLastAccessedCity(city);
await this.getWeatherAsync(city);
}
} catch (err) {
await this.setStateAsync({ weather: { error: err } } as IState);
}
};
Now run the app again, it should remember the city you input last time.
How to Use Source Code
- Install Visual Studio 2019 version 16.3 (or later version), which supports the release of .NET Core 3.0.
- Install NodeJs the latest version.
- Download and extract the source code.
- Go to "weatherclient" folder, run "
npm install
" from command line. - Open GlobalWeatherReact.sln with Visual Studio 2019, click "rebuild all", then start with "IIS Express".
Conclusion
In this article, we talked about client-side React and server-side ASP. NET Core, and REST API. We’re using Create-React-App to build react client and use .NET Core 3.0 to build API. React app and .NetCore API can be integrated seamlessly.
In part 2, we’ll start touching the fun part, React Redux. Without Redux, React only can build something small. But with Redux, React can build an enterprise app.
History
- 9th October, 2019: Initial version