<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Garry Xiao</title>
    <description>The latest articles on Forem by Garry Xiao (@garryxiao).</description>
    <link>https://forem.com/garryxiao</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F357014%2F165ce72e-9b62-4ded-b1e1-fc68b8669df8.jpg</url>
      <title>Forem: Garry Xiao</title>
      <link>https://forem.com/garryxiao</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/garryxiao"/>
    <language>en</language>
    <item>
      <title>Start Terraform from zero</title>
      <dc:creator>Garry Xiao</dc:creator>
      <pubDate>Sun, 30 Oct 2022 22:01:38 +0000</pubDate>
      <link>https://forem.com/garryxiao/start-terraform-from-zero-5gl6</link>
      <guid>https://forem.com/garryxiao/start-terraform-from-zero-5gl6</guid>
      <description>&lt;p&gt;Terraform - Automate Infrastructure on Any Cloud. &lt;a href="https://www.terraform.io/"&gt;https://www.terraform.io/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Preparation&lt;/strong&gt;:&lt;br&gt;
Visual Studio Code + HashiCorp Terraform extension&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Provider&lt;/strong&gt;&lt;br&gt;
Provider for the environment is always in the first position. We take Azure Provider as an example. The Azure Provider can be used to configure infrastructure in Microsoft Azure using the Azure Resource Manager API's. &lt;a href="https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs"&gt;https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs&lt;/a&gt;. We could create a "provider.tf" to cover the configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# We strongly recommend using the required_providers block to set the
# Azure Provider source and version being used
terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "&amp;gt;=3.0.0"
    }
  }
}

# Configure the Microsoft Azure Provider
provider "azurerm" {
  features {}
}

# Create a resource group
resource "azurerm_resource_group" "example" {
  name     = "example-resources"
  location = "West Europe"
}

# Create a virtual network within the resource group
resource "azurerm_virtual_network" "example" {
  name                = "example-network"
  resource_group_name = azurerm_resource_group.example.name
  location            = azurerm_resource_group.example.location
  address_space       = ["10.0.0.0/16"]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Provider Data&lt;/strong&gt;&lt;br&gt;
How to access current provider data? That's Data Source: azurerm_client_config. &lt;a href="https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/client_config"&gt;https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/client_config&lt;/a&gt;. Define the data and use it like the code shows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data "azurerm_client_config" "current" {
}

output "account_id" {
  value = data.azurerm_client_config.current.client_id
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Resources&lt;/strong&gt;&lt;br&gt;
Define resources you want, like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Resource Group
# https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group
resource "azurerm_resource_group" "example" {
  name     = "example"
  location = "West Europe"
}

# Key Vault
resource "azurerm_key_vault" "kv" {
  name                            = "KV"
  location                        = azurerm_resource_group.example.location
  resource_group_name             = azurerm_resource_group.example.name
  enabled_for_disk_encryption     = false
  enabled_for_deployment          = true
  enabled_for_template_deployment = true
  tenant_id                       = data.azurerm_client_config.current.tenant_id

  sku_name = "standard"
  network_acls {
    default_action = "Allow"
    bypass         = "AzureServices"
  }
}

# Service bus namespace
resource "azurerm_servicebus_namespace" "example" {
  name                = "example"
  resource_group_name = azurerm_resource_group.example.name
  location            = azurerm_resource_group.example.location
  sku                 = "Standard"
}

# Service bus topic
resource "azurerm_servicebus_topic" "example" {
  name                = "example"
  namespace_id        = azurerm_servicebus_namespace.metro60_namespace.id
  enable_partitioning = true
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Variables&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://developer.hashicorp.com/terraform/language/values/variables"&gt;https://developer.hashicorp.com/terraform/language/values/variables&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each input variable accepted by a module must be declared using a variable block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;variable "image_id" {
  type        = string
  description = "The id of the machine image (AMI) to use for the server."
  default     = "abc"
  validation {
    condition     = length(var.image_id) &amp;gt; 4 &amp;amp;&amp;amp; substr(var.image_id, 0, 4) == "ami-"
    error_message = "The image_id value must be a valid AMI id, starting with \"ami-\"."
  }
}

# Local variables within modules
locals {
  image_id_len = length(var.image_id)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we could use "var.image_id" and "local.image_id_len" for institution in the codes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Git ignore&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Exclude all .tfvars files, which are likely to contain sensitive data, such as
# password, private keys, and other secrets. These should not be part of version 
# control as they are data points which are potentially sensitive and subject 
# to change depending on the environment.
*.tfvars
*.tfvars.json

# Local .terraform directories
**/.terraform/*

# Local .tfstate files
*.tfstate
*.tfstate.*
**/override.tf
*.terraform.lock.hcl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>ETSOO Free WYSIWYG HTML Editor practice from zero</title>
      <dc:creator>Garry Xiao</dc:creator>
      <pubDate>Mon, 07 Mar 2022 07:30:19 +0000</pubDate>
      <link>https://forem.com/garryxiao/etsoo-free-wysiwyg-html-editor-practice-from-zero-ij9</link>
      <guid>https://forem.com/garryxiao/etsoo-free-wysiwyg-html-editor-practice-from-zero-ij9</guid>
      <description>&lt;p&gt;In order to complete a CMS system for ETSOO, we need a WYSIWYG HTML editor for content publish. There are a lot of components or libraries to do that but no one full covers the basic requirements we want. So we decided to continue a previous project we did serveral years ago called "EOEditor" and apply the new technologies we have to refactor it and make it a free option for the market. Leave all steps here to demostrate the pathways from zero to a workable library for your reference.&lt;/p&gt;

&lt;p&gt;Github: &lt;a href="https://github.com/ETSOO/EOEditor"&gt;https://github.com/ETSOO/EOEditor&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Init the package&lt;/strong&gt;&lt;br&gt;
'npm init' to initialize an NPM package. 'npm init -y' will not ask any question and produce the package.json file with default values. 'npm init --scope=' to create an Org scoped package.&lt;br&gt;
&lt;code&gt;npm init --scope=etsoo&lt;/code&gt;&lt;br&gt;
Changed package name to "@etsoo/editor"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Setup TypeScript&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;npm install --save-dev typescript ts-loader&lt;/code&gt;&lt;br&gt;
&lt;code&gt;npx tsc --init&lt;/code&gt;&lt;br&gt;
Changed target to "es2019".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Setup Prettier&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://prettier.io/docs/en/install.html"&gt;https://prettier.io/docs/en/install.html&lt;/a&gt;&lt;br&gt;
&lt;code&gt;npm install --save-dev --save-exact prettier&lt;/code&gt;&lt;br&gt;
Created .prettierignore and .prettierrc two files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Setup testing server&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://webpack.js.org/api/webpack-dev-server/#installation"&gt;https://webpack.js.org/api/webpack-dev-server/#installation&lt;/a&gt;&lt;br&gt;
&lt;code&gt;npm install --save-dev webpack webpack-cli webpack-dev-server&lt;/code&gt;&lt;br&gt;
Configue webpack to work with TypeScript with webpack.config.js &lt;a href="https://webpack.js.org/guides/typescript/"&gt;https://webpack.js.org/guides/typescript/&lt;/a&gt;, make sure insert the eoeditor.js(configured in output/filename) to index.html to enable "Live Reloading".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5: Editor as a resuable component&lt;/strong&gt;&lt;br&gt;
Create it as a custom HTML element. &lt;a href="https://developers.google.com/web/fundamentals/web-components/customelements"&gt;https://developers.google.com/web/fundamentals/web-components/customelements&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements"&gt;https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 6: Commands&lt;/strong&gt;&lt;br&gt;
Even thougth the 'executeCommand' is depreciated, no alternative is available. Just follow the traditional way. &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand"&gt;https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 7: Dynamic styles with var&lt;/strong&gt;&lt;br&gt;
Setup variables with same template to support dynamic update for different instances. &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties"&gt;https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 8: Editor with a Div or IFrame&lt;/strong&gt;&lt;br&gt;
At the very begining, I took the Div but gave it up later. Because a Div in same document with the buttons could not hold the selection when move the focus to dialog inputs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 9: Image edit&lt;/strong&gt;&lt;br&gt;
It's a good idea to have some client image edit functions to support basic rotate/resize features. It's done with the library &lt;a href="http://fabricjs.com/"&gt;http://fabricjs.com/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After took over 2 months of my free time, here is the first screen shot of the release.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--P1kos6E5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r03qnebluct8t0w1hg47.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P1kos6E5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r03qnebluct8t0w1hg47.jpg" alt="Image description" width="880" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Other points&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Highlight programming codes with &lt;a href="https://highlightjs.org/usage/"&gt;https://highlightjs.org/usage/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>C# "fire and forget" method</title>
      <dc:creator>Garry Xiao</dc:creator>
      <pubDate>Thu, 11 Nov 2021 09:35:35 +0000</pubDate>
      <link>https://forem.com/garryxiao/c-fire-and-forget-method-3ed</link>
      <guid>https://forem.com/garryxiao/c-fire-and-forget-method-3ed</guid>
      <description>&lt;p&gt;Option 1: async void&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        static void Main(string[] args)
        {
            try
            {
                Console.WriteLine("Main starts at " + DateTime.Now.ToLongTimeString());
                TestTask();
                Console.WriteLine("Main ends at " + DateTime.Now.ToLongTimeString() + ", " + Thread.CurrentThread.ManagedThreadId);
            }
            catch (Exception ex)
            {
                Console.WriteLine("ex: " + ex.Message);
            }

            Console.Read();
        }

        static async void TestTask()
        {
            try
            {
                Thread.Sleep(3000);
                await Task.Delay(3000);
                throw new InvalidOperationException();
            }
            catch
            {
                Console.WriteLine("TestTask exception at " + DateTime.Now.ToLongTimeString() + ", " + Thread.CurrentThread.ManagedThreadId);
            }
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Main starts at 21:12:02
Main ends at 21:12:05, 1
TestTask exception at 21:12:08, 7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Conclusions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Starting sync code inside "async void" method will be executed on the same thread of the main thread. That's "Thread.Sleep(3000)" means.&lt;/li&gt;
&lt;li&gt;Exception inside "async void" method cannot be catched if happend begins with the first async call because they are in different context. So if TestTask is without try/catch, then the application will be failed and no way to track.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Option 2: Task.Run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;       static void Main(string[] args)
        {
            try
            {
                Console.WriteLine("Main starts at " + DateTime.Now.ToLongTimeString());
                Task.Run(TestTask);
                Console.WriteLine("Main ends at " + DateTime.Now.ToLongTimeString() + ", " + Thread.CurrentThread.ManagedThreadId);
            }
            catch (Exception ex)
            {
                Console.WriteLine("ex: " + ex.Message);
            }

            Console.Read();
        }

        static void TestTask()
        {
            try
            {
                Thread.Sleep(3000);
                Task.Delay(3000);
                throw new InvalidOperationException();
            }
            catch
            {
                Console.WriteLine("TestTask exception at " + DateTime.Now.ToLongTimeString() + ", " + Thread.CurrentThread.ManagedThreadId);
            }
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Main starts at 22:14:17
Main ends at 22:14:17, 1
TestTask exception at 22:14:20, 4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Conclusions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It's real "fire and forget" from the call.&lt;/li&gt;
&lt;li&gt;Try/catch is also very important because the Main try/catch is impossible to catch the TestTask exception. But the TestTask failure does not affect the Main processing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So always avoid Async Void and use Task.Run when you want "fire and forget". From its nature, take care of the exception handle and multiple threads scenario, please do not share any resources with the main thread.&lt;/p&gt;

&lt;p&gt;FYI:&lt;br&gt;
&lt;a href="https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming"&gt;https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming&lt;/a&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
    </item>
    <item>
      <title>SQL Server huge data performance points</title>
      <dc:creator>Garry Xiao</dc:creator>
      <pubDate>Thu, 03 Sep 2020 11:41:52 +0000</pubDate>
      <link>https://forem.com/garryxiao/sql-server-huge-data-performance-points-510j</link>
      <guid>https://forem.com/garryxiao/sql-server-huge-data-performance-points-510j</guid>
      <description>&lt;p&gt;Based on the instance:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Indexes, &lt;a href="https://odetocode.com/articles/237.aspx"&gt;https://odetocode.com/articles/237.aspx&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Query Hints, &lt;a href="https://docs.microsoft.com/en-us/sql/t-sql/queries/hints-transact-sql-query?view=sql-server-ver15"&gt;https://docs.microsoft.com/en-us/sql/t-sql/queries/hints-transact-sql-query?view=sql-server-ver15&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Data Partitioning: &lt;a href="https://www.mssqltips.com/sqlservertip/1200/handling-large-sql-server-tables-with-data-partitioning/"&gt;https://www.mssqltips.com/sqlservertip/1200/handling-large-sql-server-tables-with-data-partitioning/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Computed Column to avoid unnecessary calculation: &lt;a href="https://www.mssqltips.com/sqlservertip/1682/using-computed-columns-in-sql-server-with-persisted-values/"&gt;https://www.mssqltips.com/sqlservertip/1682/using-computed-columns-in-sql-server-with-persisted-values/&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Replication&lt;br&gt;
Database replication is the technology to distribute data from the primary server to secondary servers. There are two main benefits of using SQL Server replication: 1. Using replication, we can get nearly real-time data which can be used for reporting purpose. 2. Define and schedule the replication. SQL Server supports three replication types: 1) Transactional Replication. 2) Snapshot Replication. 3) Merge Replication.SQL Server Database Replication: &lt;a href="https://codingsight.com/sql-server-database-replication"&gt;https://codingsight.com/sql-server-database-replication&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Schedule&lt;br&gt;
The schedule is a timed logic calculation. A Job in the database side: &lt;a href="https://docs.microsoft.com/en-us/sql/ssms/agent/schedule-a-job?view=sql-server-ver15"&gt;https://docs.microsoft.com/en-us/sql/ssms/agent/schedule-a-job?view=sql-server-ver15&lt;/a&gt;. Implementing a similar solution with Windows Service is also a choice.&lt;/p&gt;

&lt;p&gt;Message Queuing&lt;br&gt;
Messages can be queued and delivered later if the destination is unavailable or busy or need to consume a lot of time and external resources. Using the SQL Server Service Broker for Asynchronous Processing:&lt;br&gt;
&lt;a href="https://www.sqlshack.com/using-the-sql-server-service-broker-for-asynchronous-processing/"&gt;https://www.sqlshack.com/using-the-sql-server-service-broker-for-asynchronous-processing/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Caching&lt;br&gt;
Caching is a popular mechanism used to accelerate response times and help applications scale while reducing the load on RDBMS systems, and save on resources. Using Redis with SQL Server: &lt;a href="https://lynnlangit.com/2016/09/27/getting-started-with-sql-server-redis/"&gt;https://lynnlangit.com/2016/09/27/getting-started-with-sql-server-redis/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>sqlserver</category>
    </item>
    <item>
      <title>Create a TypeScript React notification Component from zero</title>
      <dc:creator>Garry Xiao</dc:creator>
      <pubDate>Wed, 12 Aug 2020 04:06:11 +0000</pubDate>
      <link>https://forem.com/garryxiao/create-a-typescript-react-notification-component-from-zero-30cf</link>
      <guid>https://forem.com/garryxiao/create-a-typescript-react-notification-component-from-zero-30cf</guid>
      <description>&lt;p&gt;Why not use the existing packages? They don't fulfill my requirements. I define the component to do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Existing popular features should be there.&lt;/li&gt;
&lt;li&gt;Totally written in TypeScript.&lt;/li&gt;
&lt;li&gt;A framework to work with different UI frameworks like Material-UI. This means I could totally define the appearance being consistent.&lt;/li&gt;
&lt;li&gt;Keep the codes neat and easy to understand, and easy to follow for extending.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So the task will be split into 3 parts or levels:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A pure TypeScript/JavaScript package with all features described and partially implemented.&lt;/li&gt;
&lt;li&gt;A React component based on the previous package has a full implementation.&lt;/li&gt;
&lt;li&gt;A Material-UI version to customize the appearance and behaviors.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is help link about how to create a repository: &lt;a href="https://dev.to/garryxiao/build-a-react-components-npm-package-and-ci-cd-with-github-action-1jm6"&gt;https://dev.to/garryxiao/build-a-react-components-npm-package-and-ci-cd-with-github-action-1jm6&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Task 1:&lt;br&gt;
Step 1: Create a repository at &lt;a href="https://github.com/ETSOO/NotificationBase"&gt;https://github.com/ETSOO/NotificationBase&lt;/a&gt;&lt;br&gt;
Step 2: Architect, An abstract class Notification presents the message to display. A NotificationContainer class presents a global container for the components, includes add, remove, and registered methods.&lt;/p&gt;

&lt;p&gt;Task 2:&lt;br&gt;
Step 1: Create a repository: &lt;a href="https://github.com/ETSOO/NotificationUI"&gt;https://github.com/ETSOO/NotificationUI&lt;/a&gt;&lt;br&gt;
Step 2: NotificationReact extends Notification to support under React environment. NotificationDisplay is a React component to display notifications. It will register the update method to the NotificationContainer and achieve notifications add and remove actions.&lt;/p&gt;

&lt;p&gt;Task 3:&lt;br&gt;
Step 1: Create a repository: &lt;a href="https://github.com/ETSOO/NotificationMU"&gt;https://github.com/ETSOO/NotificationMU&lt;/a&gt;&lt;br&gt;
Step 2: NotificationMU extends Notification to support Material-UI framework. NotificationDisplayMU is a Material-UI implmentation for NotificationDisplay.&lt;/p&gt;

&lt;p&gt;Task 4:&lt;br&gt;
A shared package applied: &lt;a href="https://github.com/ETSOO/Shared"&gt;https://github.com/ETSOO/Shared&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please follow the source codes to get a very basic understanding of architectural design. Use the template support of Github repository, split the requirements into 3 parts as micro packages. It may delay the development progress but would benefit the long-term maintenance and improvements.&lt;/p&gt;

</description>
      <category>react</category>
      <category>typescript</category>
    </item>
    <item>
      <title>React infinite loader with TypeScript</title>
      <dc:creator>Garry Xiao</dc:creator>
      <pubDate>Thu, 04 Jun 2020 11:57:07 +0000</pubDate>
      <link>https://forem.com/garryxiao/react-infinite-loader-with-typescript-idb</link>
      <guid>https://forem.com/garryxiao/react-infinite-loader-with-typescript-idb</guid>
      <description>&lt;p&gt;It's a performance favor solution for a list or grid of huge data. There are limited TypeScript examples to work with 'react-window-infinite-loader' and 'react-window' (A new package for react-virtualized, 'Virtualized Table', &lt;a href="https://material-ui.com/components/tables/"&gt;https://material-ui.com/components/tables/&lt;/a&gt;). I will create a functional component to work through these.&lt;/p&gt;

&lt;p&gt;For react-window&lt;br&gt;
&lt;a href="https://github.com/bvaughn/react-window/"&gt;https://github.com/bvaughn/react-window/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For react-window-infinite-loader:&lt;br&gt;
&lt;a href="https://github.com/bvaughn/react-window-infinite-loader"&gt;https://github.com/bvaughn/react-window-infinite-loader&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Encapsulated into a seperated component, reduced unnecessary properties provided with the sample:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { ComponentType } from 'react'
import { FixedSizeList, ListChildComponentProps, Layout, ListOnScrollProps, ListItemKeySelector } from 'react-window'
import InfiniteLoader from 'react-window-infinite-loader'
import { ISearchItem } from '../views/ISearchResult'

/**
 * List item renderer properties
 */
export interface ListItemRendererProps extends ListChildComponentProps {
}

/**
 * Infinite list props
 */
export interface InfiniteListProps {
    /**
     * Is horizontal layout
     */
    horizontal?: boolean

    /**
     * Height
     */
    height?: number

    /**
     * Inital scroll offset, scrollTop or scrollLeft
     */
    initialScrollOffset?: number

    /**
     * Item unit property name, default is id
     */
    itemKey?: string

    /**
     * Item renderer
     * @param props 
     */
    itemRenderer(props: ListItemRendererProps): React.ReactElement&amp;lt;ListItemRendererProps&amp;gt;

    /**
     * Item size (height)
     */
    itemSize: number

    /**
     * Load items callback
     */
    loadItems(page: number, records: number): Promise&amp;lt;ISearchItem[]&amp;gt;

    /**
     * On scroll callback
     */
    onScroll?: (props: ListOnScrollProps) =&amp;gt; any

    /**
     * Records to read onetime
     */
    records: number

    /**
     * Width
     */
    width?: string
}

/**
 * Infinite list state class
 */
class InfiniteListState {
    /**
     * List items
     */
    items: ISearchItem[]

    /**
     * All data is loaded
     */
    loaded: boolean

    /**
     * Current page
     */
    page: number

    /**
     * Constructor
     * @param items Init items
     */
    constructor(items: ISearchItem[]) {
        this.items = items
        this.loaded = false
        this.page = 0
    }
}

/**
 * Infinite list component
 * @param pros Properties
 */
export function InfiniteList(props: InfiniteListProps) {
    // Items state
    const [state, updateState] = React.useState(new InfiniteListState([]))

    // Render an item or a loading indicator
    const itemRenderer: ComponentType&amp;lt;ListChildComponentProps&amp;gt; = (lp) =&amp;gt; {
        const newProps: ListItemRendererProps = {
            data: state.items[lp.index],
            index: lp.index,
            isScrolling: lp.isScrolling,
            style: lp.style,
        }
        return props.itemRenderer(newProps)
    }

    // Determine the index is ready
    const isItemLoaded = (index: number) =&amp;gt; {
        return state.loaded || index &amp;lt; state.items.length
    }

    // Load more items
    const loadMoreItems = async (startIndex: number, stopIndex: number) =&amp;gt; {
        // Loaded then return
        if(state.loaded)
            return

        // Read next page
        const page = state.page + 1
        const items = (await props.loadItems(page, props.records)) || []

        // Add to the collection
        state.items.push(...items)

        // New state
        const newState = new InfiniteListState(state.items)
        newState.page = page
        newState.loaded = items.length &amp;lt; props.records

        // Update
        updateState(newState)
    }

    // Add 1 to the length to indicate more data is available
    const itemCount = state.items.length + (state.loaded ? 0 : 1)

    // Default calcuated height
    const height = props.height || props.records * props.itemSize

    // Default 100% width
    const width = props.width || '100%'

    // Layout
    const layout: Layout = props.horizontal ? 'horizontal'  : 'vertical'

    // Item key
    const itemKey: ListItemKeySelector = (index, data) =&amp;gt; {
        const field = props.itemKey || 'id'
        if(data == null || data[field] == null)
            return index

        return data[field]
    }

    // Return components
    return (
        &amp;lt;InfiniteLoader isItemLoaded={isItemLoaded} itemCount={itemCount} loadMoreItems={loadMoreItems} minimumBatchSize={props.records} threshold={props.records + 5}&amp;gt;
            {
                ({ onItemsRendered, ref }) =&amp;gt; (
                    &amp;lt;FixedSizeList itemCount={itemCount}
                        onItemsRendered={onItemsRendered}
                        onScroll={props.onScroll}
                        ref={ref}
                        layout={layout}
                        itemKey={itemKey}
                        initialScrollOffset={props.initialScrollOffset}
                        itemSize={props.itemSize}
                        width={width}
                        height={height}
                    &amp;gt;{itemRenderer}&amp;lt;/FixedSizeList&amp;gt;
                )
            }
        &amp;lt;/InfiniteLoader&amp;gt;
    )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An example to use it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    // Load datal
    const loadItems = async (page: number, records: number) =&amp;gt; {
        const conditions: CustomerSearchModel = { page, records }
        return (await api.searchPersonItems(conditions)).items
    }

    // Item renderer
    const itemRenderer = (props: ListItemRendererProps) =&amp;gt; {
        return (
            &amp;lt;div className={classes.listItem} style={props.style}&amp;gt;{props.index} {props.data == null ? 'Loading...' : props.data['name']}&amp;lt;/div&amp;gt;
        )
    }

&amp;lt;InfiniteList itemSize={200} records={5} height={height} loadItems={loadItems} itemRenderer={itemRenderer}/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here the property 'height' of the component in vertical case is not easy to dertermine. The full height of the document, minus the app bar height, margin or padding height, is the target height. I coded a hook to calculate two elements at the same time for calculate the real height with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  // Calculate dimensions, pass ref1 to AppBar (position="sticky"), ref2 to the outer Container
  const {ref1, ref2, dimensions1, dimensions2} = useDimensions2&amp;lt;HTMLElement, HTMLDivElement&amp;gt;(true)

  // Setup the actual pixel height
  const mainStyle = {
    height: (dimensions1 &amp;amp;&amp;amp; dimensions2 ? (dimensions2.height - dimensions1.height) : 0)
  }

/**
 * Calculate 2 elements dimensions
 * @param observeResize Is observing resize event
 */
export function useDimensions2&amp;lt;E1 extends Element, E2 extends Element&amp;gt;(observeResize: boolean = false) {
    // References for a HTML elements passed to its 'ref' property
    const ref1 = React.useRef&amp;lt;E1&amp;gt;(null)
    const ref2 = React.useRef&amp;lt;E2&amp;gt;(null)

    // Dimensions and update state
    const [dimensions, updateDimensions] = React.useState&amp;lt;DOMRect[]&amp;gt;()

    // Calcuate when layout is ready
    React.useEffect(() =&amp;gt; {
        // Update dimensions
        if(ref1.current &amp;amp;&amp;amp; ref2.current)
            updateDimensions([ref1.current.getBoundingClientRect(), ref2.current.getBoundingClientRect()])

        // Resize event handler
        const resizeHandler = (event: Event) =&amp;gt; {
            if(ref1.current &amp;amp;&amp;amp; ref2.current)
                updateDimensions([ref1.current.getBoundingClientRect(), ref2.current.getBoundingClientRect()])
        }

        // Add event listener when supported
        if(observeResize)
            window.addEventListener('resize', resizeHandler)

        return () =&amp;gt; {
            // Remove the event listener
            if(observeResize)
                window.removeEventListener('resize', resizeHandler)
        }
    }, [ref1.current, ref2.current])

    // Dimensions
    const dimensions1 = dimensions == null ? null : dimensions[0]
    const dimensions2 = dimensions == null ? null : dimensions[1]

    // Return
    return {
        ref1,
        ref2,
        dimensions1,
        dimensions2
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two additional interesting topics. How to add outer or inner elements add to the InfiniteList:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Outer element
const outerElementType = React.forwardRef&amp;lt;HTMLElement&amp;gt;((p, ref) =&amp;gt; {
    return (
        &amp;lt;Table innerRef={ref}&amp;gt;
            {p.children}
        &amp;lt;/Table&amp;gt;
    )
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>react</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Explore data system design with C# and SQL Server</title>
      <dc:creator>Garry Xiao</dc:creator>
      <pubDate>Tue, 05 May 2020 05:38:20 +0000</pubDate>
      <link>https://forem.com/garryxiao/explore-data-system-design-with-c-and-sql-server-3apg</link>
      <guid>https://forem.com/garryxiao/explore-data-system-design-with-c-and-sql-server-3apg</guid>
      <description>&lt;p&gt;A data system means there could be massive and various data and also focus on data. We want to design the system to control the logic for user authentication and data input. It's a common part of all kinds of information systems. I would like to explore any possibility to have some standards for designing it.&lt;/p&gt;

&lt;p&gt;You will find it's challenging to design a system from zero during the junior and intermediate stages of your career, no way to start or confused by different kinds of patterns or options. How to choose a programming language? How to choose the database? How to adapt to expansion and different scales? You need hands-on experiences, but without a valuable guide before it will be a nightmare. The code is getting bloated, and the cost of refactoring is getting bigger and bigger. Until the day of giving up, you don’t necessarily understand why this happens.&lt;/p&gt;

&lt;p&gt;Is coding language important? Yes but not the key. Everyone will follow his or her strengths, as a senior role, you should understand the language neutral, just like the natural languages around the world, they all play well with the communication role. You could choose any language that exists, or develop language if you want (it's a joke). Frankly speaking, during a real project, you need to make the decision on your or the team's strengths or favorites. In recent years, Microsoft has been running in the right direction. .NET 5.0 will be a milestone. I am sure C# will be a good choice in the coming years. Another tech giant Google has developed the realm of development language through Node.js and Golang which also are good candidates. I have little positive comments for Java but I don't mean refuse to accept it.&lt;/p&gt;

&lt;p&gt;Which database solution is better? SQL Server is the best choice under Microsoft stacks. MySQL is also good if you like it. I once used the Oracle, it's powerful but the question is why I need so much. How about MongoDB? Under stable structured data circumstances, not a good idea. Structured Query Language (SQL) is a wide applied standard. Any relational database could be the choice if you like. NoSQL database in specific cases like dealing with storing JSON-like documents is a winner. There is a wide range of developer ecology, which is very important, because no matter whether the product is good or not, it needs people to work on it.&lt;/p&gt;

&lt;p&gt;OK, let's start to design the system with C# and SQL Server. There are 3 points need to be considered with any system. First is the configuration, make sure the system is configurable. The second is storage, local hard drive storage is common, cloud storage is also an option. The last is the database and the data layer interface. Maybe more but they are very typical and enough for illustration.&lt;/p&gt;

&lt;p&gt;Configuration or options are common to be designed with read-only properties of a  class object. A builder pattern is useful to initialize the properties as showed below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    /// &amp;lt;summary&amp;gt;
    /// Common configuration for application
    /// 程序对象的通用配置
    /// &amp;lt;/summary&amp;gt;
    public abstract class ConfigurationBase : ICoreConfiguration
    {
        /// &amp;lt;summary&amp;gt;
        /// Fluent builder base
        /// 液态基础构建器
        /// &amp;lt;/summary&amp;gt;
        public abstract class BuilderBase&amp;lt;T&amp;gt; where T : ConfigurationBase
        {
            /// &amp;lt;summary&amp;gt;
            /// Configuration base
            /// 基础配置对象
            /// &amp;lt;/summary&amp;gt;
            private T Configuration;

            /// &amp;lt;summary&amp;gt;
            /// Constructor
            /// 构造函数
            /// &amp;lt;/summary&amp;gt;
            /// &amp;lt;param name="configuration"&amp;gt;Configuration&amp;lt;/param&amp;gt;
            protected BuilderBase(T configuration)
            {
                Configuration = configuration;
            }

            /// &amp;lt;summary&amp;gt;
            /// Build the configuration
            /// 创建配置对象
            /// &amp;lt;/summary&amp;gt;
            /// &amp;lt;returns&amp;gt;Configuration&amp;lt;/returns&amp;gt;
            public T Build()
            {
                return Configuration;
            }

            /// &amp;lt;summary&amp;gt;
            /// Whether model is validated
            /// 模块是否已经通过验证
            /// &amp;lt;/summary&amp;gt;
            /// &amp;lt;param name="validated"&amp;gt;Validated&amp;lt;/param&amp;gt;
            /// &amp;lt;returns&amp;gt;Builder&amp;lt;/returns&amp;gt;
            public BuilderBase&amp;lt;T&amp;gt; ModelValidated(bool validated)
            {
                Configuration.ModelValidated = validated;
                return this;
            }

            /// &amp;lt;summary&amp;gt;
            /// Set private deployment
            /// 设置私有化部署
            /// &amp;lt;/summary&amp;gt;
            /// &amp;lt;param name="id"&amp;gt;Private delopyment id, to avoid same encription result with same private key&amp;lt;/param&amp;gt;
            /// &amp;lt;returns&amp;gt;Builder&amp;lt;/returns&amp;gt;
            public BuilderBase&amp;lt;T&amp;gt; PrivateDeployment(string id)
            {
                Configuration.PrivateDeploymentId = id;
                return this;
            }

            /// &amp;lt;summary&amp;gt;
            /// Set service user
            /// 设置服务账号
            /// &amp;lt;/summary&amp;gt;
            /// &amp;lt;param name="id"&amp;gt;Service user id&amp;lt;/param&amp;gt;
            /// &amp;lt;returns&amp;gt;Builder&amp;lt;/returns&amp;gt;
            public BuilderBase&amp;lt;T&amp;gt; ServiceUser(string id)
            {
                Configuration.ServiceUserId = id;
                return this;
            }

            /// &amp;lt;summary&amp;gt;
            /// Set keys
            /// 设置键
            /// &amp;lt;/summary&amp;gt;
            /// &amp;lt;param name="privateKey"&amp;gt;Private key for encrption&amp;lt;/param&amp;gt;
            /// &amp;lt;param name="symmetricKey"&amp;gt;Symmetric security key, for exchange&amp;lt;/param&amp;gt;
            /// &amp;lt;returns&amp;gt;Builder&amp;lt;/returns&amp;gt;
            public BuilderBase&amp;lt;T&amp;gt; SetKeys(string privateKey, string symmetricKey)
            {
                Configuration.PrivateKey = privateKey;
                Configuration.SymmetricKey = symmetricKey;
                return this;
            }
        }

        /// &amp;lt;summary&amp;gt;
        /// Flag for identification, default is 'e', like stored procedure name will start with it
        /// 标识值，默认值为 'e'，比如存储过程会以该字母打头
        /// &amp;lt;/summary&amp;gt;
        public virtual string Flag
        {
            get { return "e"; }
        }

        /// &amp;lt;summary&amp;gt;
        /// Model DataAnnotations are validated, true under Web API 3 to avoid double validation
        /// 模块数据标记已验证，在Web API 3下可以设置为true以避免重复验证
        /// &amp;lt;/summary&amp;gt;
        public bool ModelValidated { get; private set; }

        private string privateDeploymentId;
        /// &amp;lt;summary&amp;gt;
        /// Private delopyment id, to avoid same encription result with same private key
        /// 私有部署编号，避免多个平台相同加密私匙导致加密结果一样
        /// &amp;lt;/summary&amp;gt;
        public string PrivateDeploymentId
        {
            get { return privateDeploymentId; }
            private set
            {
                if (value == null)
                    value = string.Empty;

                privateDeploymentId = value;
            }
        }

        /// &amp;lt;summary&amp;gt;
        /// Private key for encrption
        /// 加密私匙
        /// &amp;lt;/summary&amp;gt;
        public string PrivateKey { get; private set; }

        /// &amp;lt;summary&amp;gt;
        /// Service user id
        /// 服务用户编号
        /// &amp;lt;/summary&amp;gt;
        public string ServiceUserId { get; private set; }

        /// &amp;lt;summary&amp;gt;
        /// Symmetric security key, for exchange
        /// 对称安全私匙，用于交换
        /// &amp;lt;/summary&amp;gt;
        public string SymmetricKey { get; private set; }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Designed with abstract class and support the generic builder, is target to be extended easily:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    /// &amp;lt;summary&amp;gt;
    /// Main configuration
    /// 扩展的配置
    /// &amp;lt;/summary&amp;gt;
    public class MainConfiguration : ConfigurationBase
    {
        /// &amp;lt;summary&amp;gt;
        /// Main configuration builder
        /// 扩展的配置器
        /// &amp;lt;/summary&amp;gt;
        public class Builder : BuilderBase&amp;lt;MainConfiguration&amp;gt;
        {
            /// &amp;lt;summary&amp;gt;
            /// Constructor
            /// 构造函数
            /// &amp;lt;/summary&amp;gt;
            public Builder() : base(new MainConfiguration())
            {
            }
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is always a Application, holds the configuration, database and storage resources, could be initialized in singleton or as a static object. An interface of the application demonstrated below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    /// &amp;lt;summary&amp;gt;
    /// Application interface
    /// 程序接口
    /// &amp;lt;/summary&amp;gt;
    public interface IApplication
    {
        /// &amp;lt;summary&amp;gt;
        /// Configuration
        /// 配置
        /// &amp;lt;/summary&amp;gt;
        ICoreConfiguration Configuration { get; }

        /// &amp;lt;summary&amp;gt;
        /// Database
        /// 数据库
        /// &amp;lt;/summary&amp;gt;
        ICommonDatabase Database { get; }

        /// &amp;lt;summary&amp;gt;
        /// Storage
        /// 存储
        /// &amp;lt;/summary&amp;gt;
        IStorage Storage { get; }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For data access layer, Entity Framework (EF) is somehow easier but will lose some  performance. I prefer to deal with traditional ADO.NET way.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    /// &amp;lt;summary&amp;gt;
    /// SQL Server Database
    /// SQL Server 数据库
    /// &amp;lt;/summary&amp;gt;
    public class SqlServerDatabase : CommonDatabase
    {
        /// &amp;lt;summary&amp;gt;
        /// New connection
        /// 新链接对象
        /// &amp;lt;/summary&amp;gt;
        public SqlConnection NewConnection
        {
            get { return new SqlConnection(ConnectionString); }
        }

        /// &amp;lt;summary&amp;gt;
        /// Constructor
        /// 构造函数
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name="connectionString"&amp;gt;Database connection string&amp;lt;/param&amp;gt;
        public SqlServerDatabase(string connectionString) : base(connectionString)
        {
        }

        /// &amp;lt;summary&amp;gt;
        /// Add parameters to command
        /// DBNull.Value for non-empty NULL
        /// 给命令添加参数
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name="command"&amp;gt;Command&amp;lt;/param&amp;gt;
        /// &amp;lt;param name="paras"&amp;gt;Parameters&amp;lt;/param&amp;gt;
        public void AddParameters(SqlCommand command, IDictionary&amp;lt;string, dynamic&amp;gt; paras)
        {
            if (paras == null)
                return;

            command.Parameters.AddRange(paras.Where(item =&amp;gt; item.Value != null).Select(item =&amp;gt;
            {
                if (item.Value is SqlParameter p)
                    return p;
                else
                    return new SqlParameter(item.Key, item.Value);
            }).ToArray());
        }

        /// &amp;lt;summary&amp;gt;
        /// Create EF Database Context
        /// 创建EF数据库上下文
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;typeparam name="M"&amp;gt;Model class&amp;lt;/typeparam&amp;gt;
        /// &amp;lt;returns&amp;gt;Database Context&amp;lt;/returns&amp;gt;
        public override CommonDbContext&amp;lt;M&amp;gt; CreateContext&amp;lt;M&amp;gt;()
        {
            return new SqlServerDbContext&amp;lt;M&amp;gt;(ConnectionString);
        }

        /// &amp;lt;summary&amp;gt;
        /// Execute SQL Command, return rows affacted
        /// 执行SQL命令，返回影响的行数
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name="sql"&amp;gt;SQL Command&amp;lt;/param&amp;gt;
        /// &amp;lt;param name="paras"&amp;gt;Parameters&amp;lt;/param&amp;gt;
        /// &amp;lt;param name="isStoredProcedure"&amp;gt;Is stored procedure&amp;lt;/param&amp;gt;
        /// &amp;lt;returns&amp;gt;Rows affacted&amp;lt;/returns&amp;gt;
        public override int Execute(string sql, IDictionary&amp;lt;string, dynamic&amp;gt; paras, bool? isStoredProcedure = false)
        {
            using (var connection = NewConnection)
            using (var command = new SqlCommand(sql, connection))
            {
                // Add parameters
                AddParameters(command, paras);

                // Command type
                if (isStoredProcedure == null)
                    command.Prepare();
                else if (isStoredProcedure.Value)
                    command.CommandType = CommandType.StoredProcedure;

                // Open connection
                connection.Open();

                // Execute
                var result = command.ExecuteNonQuery();

                // Close
                connection.Close();

                // Return
                return result;
            }
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But still provide the EF support with the method CreateContext with SqlServerDbContext below. Please do not make confused with Common* things, I just added multiple databases support, like SQLite or MySQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    /// &amp;lt;summary&amp;gt;
    /// SQL Server EF Database Context
    /// SQL Server EF 数据库上下文
    /// &amp;lt;/summary&amp;gt;
    public class SqlServerDbContext&amp;lt;M&amp;gt; : CommonDbContext&amp;lt;M&amp;gt; where M : class
    {
        private string connectionString;

        /// &amp;lt;summary&amp;gt;
        /// Constructor
        /// 构造函数
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name="connectionString"&amp;gt;Connection string&amp;lt;/param&amp;gt;
        public SqlServerDbContext(string connectionString)
        {
            this.connectionString = connectionString;
        }

        /// &amp;lt;summary&amp;gt;
        /// Override OnConfiguring to setup
        /// 重写配置初始化
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name="optionsBuilder"&amp;gt;Options builder&amp;lt;/param&amp;gt;
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(this.connectionString);
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How to deal with actions or the business logic? For example, the user's login and CRUD. I always put them into a user service. A service is a realm of particular requirements, linked with the application and current user. For example, for the login action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    /// &amp;lt;summary&amp;gt;
    /// User service
    /// 用户服务
    /// &amp;lt;/summary&amp;gt;
    public sealed class UserSerivce : LoginService
    {
        /// &amp;lt;summary&amp;gt;
        /// Create user service
        /// 创建用户服务
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name="app"&amp;gt;Application&amp;lt;/param&amp;gt;
        /// &amp;lt;param name="user"&amp;gt;Current user&amp;lt;/param&amp;gt;
        /// &amp;lt;returns&amp;gt;User service&amp;lt;/returns&amp;gt;
        public static UserSerivce Create(IMainApp app, ICurrentUser user)
        {
            var service = new UserSerivce();
            service.Application = app;
            service.User = user;
            return service;
        }

        /// &amp;lt;summary&amp;gt;
        /// Private constructor to prevent initialization
        /// 私有的构造函数防止实例化
        /// &amp;lt;/summary&amp;gt;
        private UserSerivce()
        {

        }

        private OperationData GetLoginData(LoginModel model)
        {
            // Create operation data
            var data = CreateOperationData("login");

            // Parameterize modeal
            model.Parameterize(data, this);

            // Return
            return data;
        }

        /// &amp;lt;summary&amp;gt;
        /// Async user login
        /// 异步用户登录
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name="model"&amp;gt;Data model&amp;lt;/param&amp;gt;
        /// &amp;lt;returns&amp;gt;Action result&amp;lt;/returns&amp;gt;
        public async Task&amp;lt;OperationResult&amp;gt; LoginAsync(LoginModel model)
        {
            // Validate model
            var result = ValidateModel(model);

            // Invalid result return anyway
            if (!result.OK)
                return result;

            // Access database to valid
            return await ExecuteAsync(GetLoginData(model), false);
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use private constructor to hide the initialization directly and provide a static method to connect the application and user when creation. Model holds the data and validation rules, method Parameterize just collect the data into database parameters then submit to database side stored procedure to process, for the login model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    /// &amp;lt;summary&amp;gt;
    /// Login model
    /// https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations?view=netframework-4.8
    /// 登录模型
    /// &amp;lt;/summary&amp;gt;
    public class LoginModel : LoginService.DataModel
    {
        [StringLength(8, MinimumLength = 4)]
        /// &amp;lt;summary&amp;gt;
        /// Veryfication code
        /// 验证码
        /// &amp;lt;/summary&amp;gt;
        public string Code { get; set; }

        /// &amp;lt;summary&amp;gt;
        /// Id, like number id, mobile, or email
        /// 编号，像数字编号，手机号码或邮箱
        /// &amp;lt;/summary&amp;gt;
        [Required]
        [StringLength(256, MinimumLength = 4)]
        public string Id { get; set; }

        /// &amp;lt;summary&amp;gt;
        /// Id type
        /// 编号类型
        /// &amp;lt;/summary&amp;gt;
        public LoginIdType? IdType { get; set; }

        /// &amp;lt;summary&amp;gt;
        /// Language cid, like Simplified Chinese zh-CN
        /// 语言编号，如简体中文 zh-CN
        /// &amp;lt;/summary&amp;gt;
        [RegularExpression(@"^[a-z]{2}(-[A-Z]{2})?$")]
        public string LanguageCid { get; set; }

        /// &amp;lt;summary&amp;gt;
        /// Current organization id
        /// 当前限定的登录机构
        /// &amp;lt;/summary&amp;gt;
        public int? Org { get; set; }

        /// &amp;lt;summary&amp;gt;
        /// Raw password
        /// 原始密码
        /// &amp;lt;/summary&amp;gt;
        [Required]
        [StringLength(30, MinimumLength = 4)]
        public string Password { get; set; }

        /// &amp;lt;summary&amp;gt;
        /// Is save login
        /// 是否保存登录
        /// &amp;lt;/summary&amp;gt;
        public bool? Save { get; set; }

        private void CalculateIdType()
        {
            if (IdType == null || IdType == LoginIdType.Unknown)
            {
                if (Id.Contains("@"))
                {
                    IdType = LoginIdType.Email;
                }
                else
                {
                    if (Regex.IsMatch(Id, @"^\d+$"))
                    {
                        // Starting with zero or its length more than 10 presents mobile phone number
                        // 以0开头，或者长度大于10位，已经不方便通过数字编号记忆，识别为移动手机号码
                        if (Id.StartsWith("0") || Id.Length &amp;gt;= 10)
                        {
                            IdType = LoginIdType.Mobile;
                        }
                        else
                        {
                            IdType = LoginIdType.Id;
                        }
                    }
                    else
                    {
                        IdType = LoginIdType.Cid;
                    }
                }
            }
        }

        /// &amp;lt;summary&amp;gt;
        /// Override to collect parameters
        /// 重写收集参数
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name="data"&amp;gt;Data&amp;lt;/param&amp;gt;
        /// &amp;lt;param name="service"&amp;gt;Service&amp;lt;/param&amp;gt;
        public override void Parameterize(OperationData data, IService&amp;lt;int&amp;gt; service)
        {
            base.Parameterize(data, service);

            // Calculate id type
            CalculateIdType();

            // Add parameters
            var paras = data.Parameters;

            // Verification code
            paras.Add("has_code", true);

            paras.Add("id", Id);
            paras.Add("id_type", (byte)IdType);
            paras.Add("ip", service.User.ClientIp.ToString());
            paras.Add("language_cid", LanguageCid ?? service.User.LanguageCid);

            // Login method, 1 = Default
            paras.Add("method", 1);

            paras.Add("org", Org);

            // Hash password
            paras.Add("password", service.Application.HashPassword(Password));

            paras.Add("save_login", Save.GetValueOrDefault());
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the database side, there is a stored procedure to deal with parameters and execute to provide a result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE PROCEDURE [dbo].[ep_user_login]
-- ================================================
--
-- User login，Error：90006
-- 
-- ================================================
    @id_type tinyint,                 -- Id type
    @id varchar(256),                 -- User number id, email or mobile
    @password varchar(256),           -- Password encrypted
    @has_code bit,                    -- Is recaptu
    @save_login bit,                  -- Save login
    @language_id smallint = NULL,     -- Language id
    @language_cid varchar(5) = NULL,  -- Language cid
    @org int = NULL,                  -- Organization limited
    @origin varchar(256) = NULL,      -- CORS origin
    @service_user_id int = NULL,      -- Service user id running

    @ip varchar(45),                  -- IP
    @method tinyint                   -- Login method
AS
BEGIN
    SET NOCOUNT ON;

    -- Error code
    DECLARE @error_code int = 90006;

    -- Default language id
    DECLARE @default_language_id smallint = dbo.ef_get_login_language_id(@language_id, @language_cid, 0);

    -- If there is no user, return initialization tip
    IF NOT EXISTS(SELECT * FROM e_system_setting WHERE initialized = 1)
        BEGIN
            SELECT @error_code AS [error_code], 'initialization' AS m_id, dbo.ef_get_translation('initialization', @default_language_id) AS [message];
            RETURN;
        END
    ....
END
GO
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to save energies with the core system and Web API integration. I would like to share the model. The API side code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        /// &amp;lt;summary&amp;gt;
        /// Login for authentication
        /// 登录授权
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name="model"&amp;gt;Data model&amp;lt;/param&amp;gt;
        /// &amp;lt;returns&amp;gt;Result&amp;lt;/returns&amp;gt;
        [AllowAnonymous]
        [HttpPost("Login")]
        public async Task Login(LoginModel model)
        {
            // Act
            var result = await Service.LoginAsync(model);

            if (result.OK)
            {
                // Logined user id
                var userId = result.Data.Get("token_user_id", 0);

                // User role
                var role = result.Data.Get("role", UserRole.User);

                // Hold the token value and then return to client
                result.Data["authorization"] = CreateToken(userId, role);

                // Suggested refresh seconds
                result.Data["refresh_seconds"] = 300;
            }

            // Output
            await ResultContentAsync(result);
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to speed up all its best, any 'new' generic constraint related to Activator.CreateInstance avoided. Put logic heavily on the database side with the power of stored procedures will benefit data manipulation. Code snippets like hills stop you from viewing the panorama.&lt;/p&gt;

&lt;p&gt;Two issues I met listed here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Web deploy publishes wrong version of assembly, always lower than debug version (&lt;a href="https://stackoverflow.com/questions/32657241/web-deploy-publishes-wrong-version-of-assembly/33223619#comment110408941_33223619"&gt;https://stackoverflow.com/questions/32657241/web-deploy-publishes-wrong-version-of-assembly/33223619#comment110408941_33223619&lt;/a&gt;). With VS 2019, .Net Core 3.1, delete the folder 'obj\Release\netcoreapp3.1\win-x64\R2R'.&lt;/li&gt;
&lt;li&gt;SQL Server json truncated (&lt;a href="https://stackoverflow.com/questions/51087037/sql-server-json-truncated-even-when-using-nvarcharmax"&gt;https://stackoverflow.com/questions/51087037/sql-server-json-truncated-even-when-using-nvarcharmax&lt;/a&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I draw a diagram here for your reference:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Rx314vX2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/gqp79l7837envq1maxrm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Rx314vX2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/gqp79l7837envq1maxrm.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope it will provide you some valuable ideas when designing a system from zero.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>sqlserver</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Form validation with Yup under React and Material-UI</title>
      <dc:creator>Garry Xiao</dc:creator>
      <pubDate>Sat, 02 May 2020 11:37:35 +0000</pubDate>
      <link>https://forem.com/garryxiao/form-validation-with-yup-under-react-and-material-ui-4nhg</link>
      <guid>https://forem.com/garryxiao/form-validation-with-yup-under-react-and-material-ui-4nhg</guid>
      <description>&lt;p&gt;In a real project, facing the form validation soon begin on frontend coding. After several rounds of refactoring, I completed it with 4 points in my project:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Totally TypeScript&lt;/li&gt;
&lt;li&gt;Speed up development with depository support&lt;/li&gt;
&lt;li&gt;Custom hook&lt;/li&gt;
&lt;li&gt;Minimum refactoring for components&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Chose Yup for validation schema definition, it is simple and easy to understand:&lt;br&gt;
&lt;a href="https://github.com/jquense/yup"&gt;https://github.com/jquense/yup&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -S yup
npm install -D @types/yup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;React custom hook is a common function, with parameters for input and return necessary tool methods. useFormValidator as below is a custom hook and only rely on packages "react" and "yup", no relationship with the Material-UI framework:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from "react"
import * as Yup from 'yup'

/**
 * Form validator state field
 */
interface FormValidatorStateField {
    /**
     * Is error state
     */
    error: boolean

    /**
     * state text
     */
    text: string
}

/**
 * Form validator state fields
 */
interface FormValidatorStateFields {
    [key: string]: FormValidatorStateField
}

/**
 * Form validatior
 * @param schemas Initial validation schemas
 * @param milliseconds Merge change update interval
 */
export const useFormValidator = (schemas: Yup.ObjectSchema&amp;lt;object&amp;gt;, milliseconds: number = 200) =&amp;gt; {
    // useState init
    const defaultState: FormValidatorStateFields = {}
    const [state, updateState] = React.useState&amp;lt;FormValidatorStateFields&amp;gt;(defaultState)

    // Change timeout seed
    let changeSeed = 0

    // Change value handler
    const commitChange = (field: string, value: any) =&amp;gt; {
        // Validate the field, then before catch, if catch before then, both will be triggered
        Yup.reach(schemas, field).validate(value).then(result =&amp;gt; {
            commitResult(field, result)
        }).catch(result =&amp;gt; {
            commitResult(field, result)
        })
    }

    // Commit state result
    const commitResult = (field: string, result: any) =&amp;gt; {
        let currentItem = state[field]
        if(result instanceof Yup.ValidationError) {
            // Error
            if(currentItem) {
                // First to avoid same result redraw
                if(currentItem.error &amp;amp;&amp;amp; currentItem.text == result.message)
                    return

                // Update state
                currentItem.error = true
                currentItem.text = result.message
            } else {
                // New item
                const newItem: FormValidatorStateField = {
                    error: true,
                    text: result.message
                }
                state[field] = newItem
            }
        } else {
            // Success and no result, just continue
            if(currentItem == null)
                return

            // Delete current state result
            delete state[field]
        }

        // Update state, for object update, need a clone
        const newState = {...state}
        updateState(newState)
    }

    // Clear timeout seed
    const clearSeed = () =&amp;gt; {
        if(changeSeed &amp;gt; 0)
            clearTimeout(changeSeed)
    }

    // Delay change
    const delayChange = (field: string, value: any) =&amp;gt; {
        clearSeed()

        changeSeed = setTimeout(() =&amp;gt; {
            commitChange(field, value)
        }, milliseconds)
    }

    // Merge into the life cycle
    React.useEffect(() =&amp;gt; {
        return () =&amp;gt; {
            // clearTimeout before dispose the view
            clearSeed()
        }
    }, [])

    // Return methods for manipulation
    return {
        /**
         * Input or Textarea blur handler
         * @param event Focus event
         */
        blurHandler: (event: React.FocusEvent&amp;lt;HTMLInputElement | HTMLTextAreaElement&amp;gt;) =&amp;gt; {
            const { name, value } = event.currentTarget
            delayChange(name, value)
        },

        /**
         * Input or Textarea change handler
         * @param event Change event
         */
        changeHandler: (event: React.ChangeEvent&amp;lt;HTMLInputElement | HTMLTextAreaElement&amp;gt;) =&amp;gt; {
            const { name, value } = event.currentTarget
            delayChange(name, value)
        },

        /**
         * Commit change
         */
        commitChange: commitChange,

        /**
         * State error or not
         * @param field Field name
         */
        errors: (field: string) =&amp;gt; {
            return state[field]?.error
        },

        /**
         * State text
         * @param field Field name
         */
        texts: (field: string) =&amp;gt; {
            return state[field]?.text
        },

        /**
         * Validate form data
         * @param data form data, Object.fromEntries(new FormData(form))
         */
        validate: async (data: any) =&amp;gt; {
            try
            {
                clearSeed()
                return await schemas.validate(data, { strict: true, abortEarly: false, stripUnknown: false })
            }
            catch(e)
            {
                // Reset
                const newState: FormValidatorStateFields = {}

                // Iterate the error items
                if(e instanceof Yup.ValidationError) {
                    for(let error of e.inner) {
                        // Only show the first error of the field
                        if(newState[error.path] == null) {
                            // New item
                            const newItem: FormValidatorStateField = {
                                error: true,
                                text: error.message
                            }

                            newState[error.path] = newItem
                        }
                    }
                }

                // Update state
                updateState(newState)
            }

            return null
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When use it in Materal-UI pages, for example a login page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Login component
function Login() {

    // Form validator
    const { blurHandler, changeHandler, errors, texts, validate } = useFormValidator(validationSchemas)

    // Login action
    async function doLogin(event: React.FormEvent&amp;lt;HTMLFormElement&amp;gt;) {
        // Prevent default action
        event.preventDefault()

        // Form JSON data
        let data = await validate(Object.fromEntries(new FormData(event.currentTarget)))
        if(data == null)
          return

        // Local data format

        // Parase as model
        const model = data as LoginModel
   }

    return (
        &amp;lt;Container component="main" maxWidth="xs"&amp;gt;
        &amp;lt;CssBaseline /&amp;gt;
        &amp;lt;img src={window.location.origin + '/logo.jpg'} alt="Logo" className={classes.logo}/&amp;gt;
        &amp;lt;div className={classes.paper}&amp;gt;
          &amp;lt;Avatar className={classes.avatar}&amp;gt;
            &amp;lt;LockOutlined /&amp;gt;
          &amp;lt;/Avatar&amp;gt;
          &amp;lt;Typography component="h1" variant="h5"&amp;gt;
            Sign in
          &amp;lt;/Typography&amp;gt;
          &amp;lt;form className={classes.form} onSubmit={doLogin} noValidate&amp;gt;
            &amp;lt;TextField
              variant="outlined"
              margin="normal"
              required
              fullWidth
              id="id"
              label="Id or Email"
              name="id"
              error={errors('id')}
              helperText={texts('id')}
              onChange={changeHandler}
              onBlur={blurHandler}
              autoComplete="email"
              autoFocus
            /&amp;gt;
            &amp;lt;TextField
              variant="outlined"
              margin="normal"
              type="password"
              required
              fullWidth
              name="password"
              error={errors('password')}
              helperText={texts('password')}
              onChange={changeHandler}
              onBlur={blurHandler}
              label="Password"
              id="password"
              autoComplete="current-password"
            /&amp;gt;
            &amp;lt;FormControlLabel
              control={&amp;lt;Checkbox name="save" value="true" color="primary" /&amp;gt;}
              label="Remember me"
            /&amp;gt;
            &amp;lt;Button
              type="submit"
              fullWidth
              variant="contained"
              color="primary"
              className={classes.submit}
            &amp;gt;
              Sign In
            &amp;lt;/Button&amp;gt;
          &amp;lt;/form&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/Container&amp;gt;
    )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First declear the validation schemas, initialize 'useFormValidator' and accept the returned methods for binding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;              error={errors('password')}
              helperText={texts('password')}
              onChange={changeHandler}
              onBlur={blurHandler}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Through bindings only to current components to indicate any validation errors occur. No refactoring or extending for current components. That's the key feature of the task I enjoyed.&lt;/p&gt;

</description>
      <category>react</category>
      <category>materialui</category>
      <category>validation</category>
    </item>
    <item>
      <title>TypeScript extending ReactJs component from basic</title>
      <dc:creator>Garry Xiao</dc:creator>
      <pubDate>Wed, 29 Apr 2020 11:24:20 +0000</pubDate>
      <link>https://forem.com/garryxiao/typescript-extending-reactjs-component-from-basic-topics-cl1</link>
      <guid>https://forem.com/garryxiao/typescript-extending-reactjs-component-from-basic-topics-cl1</guid>
      <description>&lt;p&gt;From the official article (&lt;a href="https://reactjs.org/docs/composition-vs-inheritance.html" rel="noopener noreferrer"&gt;https://reactjs.org/docs/composition-vs-inheritance.html&lt;/a&gt;), it recommends using composition instead of inheritance to reuse code between components. As the support of hooks with functional components, that's the trends, especially with the support of TypeScript, will make things amazing.&lt;/p&gt;

&lt;p&gt;Start simplest example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

function TestComponent() {
  return (
    &amp;lt;h1&amp;gt;Hello, world!&amp;lt;/h1&amp;gt;
  )
}

ReactDOM.render(
  &amp;lt;TestComponent /&amp;gt;,
  document.getElementById('root')
)


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As you see, a functional component is just a function with return. As required, User-Defined Components Must Be Capitalized. Here JSX codes used, with TypeScript file extension 'tsx'. Each JSX element, like &lt;code&gt;&amp;lt;h1&amp;gt;Hello,&lt;br&gt;
&lt;br&gt;
 world!&amp;lt;/h1&amp;gt;&lt;br&gt;
&lt;br&gt;
&lt;/code&gt; is just syntactic sugar for calling React.createElement(component, props, ...children), as &lt;code&gt;React.createElement('h1',&lt;br&gt;
&lt;br&gt;
 null, 'Hello, world!')&lt;br&gt;
&lt;br&gt;
&lt;/code&gt;. So, anything you can do with JSX can also be done with just plain JavaScript. More details please view &lt;a href="https://reactjs.org/docs/jsx-in-depth.html" rel="noopener noreferrer"&gt;https://reactjs.org/docs/jsx-in-depth.html&lt;/a&gt;. So the example below with pure TypeScript (file extension could be .ts) is equal:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

function TestComponent() {
  return React.createElement('h1', null, 'Hello, world!')
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Property support is common. We make the example a little complex:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

interface TestComponentProps {
  name?: string
}

function TestComponent(props: TestComponentProps) {
  return (
    &amp;lt;h1&amp;gt;{props.name || 'Unknown'} - Hello, world!&amp;lt;/h1&amp;gt;
  )
}

ReactDOM.render(
  &amp;lt;TestComponent name="Garry" /&amp;gt;,
  document.getElementById('root')
)


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Under TypeScript, we use an interface or a type to define the properties. The nullable property adds a '?' after the property name. Now the component can accept the property 'name' and change the output accordingly. You can add any other properties as you want.&lt;/p&gt;

&lt;p&gt;It's not necessary to write everything during development. There are UI frameworks who have made a lot of effort to speed up the process. Like Material-UI (&lt;a href="https://material-ui.com/" rel="noopener noreferrer"&gt;https://material-ui.com/&lt;/a&gt;) or Antd (&lt;a href="https://ant.design" rel="noopener noreferrer"&gt;https://ant.design&lt;/a&gt;). Just follow the documentation, understand each component, practice them, and will be handy. Then custom a component would be necessary. Here will make an extended TestComponent:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

interface TestComponentProps {
  name?: string
}

function TestComponent(props: TestComponentProps) {
  return (
    &amp;lt;h1&amp;gt;{props.name || 'Unknown'} - Hello, world!&amp;lt;/h1&amp;gt;
  )
}

interface TestComponentNewProps extends TestComponentProps {
  age?: number
}

function TestComponentNew(props: TestComponentNewProps) {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;TestComponent {...props}/&amp;gt;
      &amp;lt;h2&amp;gt;Age: {props.age}&amp;lt;/h2&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}

ReactDOM.render(
  &amp;lt;TestComponentNew name="Garry" age="40" /&amp;gt;,
  document.getElementById('root')
)


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;How to extend a Material-UI component? We change the previous example to extend the Button component:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import React from "react"
import ReactDOM from "react-dom"
import Button, { ButtonProps } from "@material-ui/core/Button"

const TestComponentNew : React.FunctionComponent&amp;lt;ButtonProps&amp;gt; = (props) =&amp;gt; {
  props = Object.assign({ variant: 'contained' }, props)
  return (
    &amp;lt;Button {...props}&amp;gt;{props.children}&amp;lt;/Button&amp;gt;
  )
}

ReactDOM.render(
  &amp;lt;div&amp;gt;
    &amp;lt;Button variant="contained"&amp;gt;Source button&amp;lt;/Button&amp;gt;
    &amp;lt;br/&amp;gt;
    &amp;lt;TestComponentNew&amp;gt;Extended button&amp;lt;/TestComponentNew&amp;gt;
  &amp;lt;/div&amp;gt;,
  document.getElementById('root')
)


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The key point is to use 'React.FunctionComponent' to extend and pass ButtonProps as a strong type for the generic method. Then you could use props.children and other properties inside. It's impossible to set properties directly but could use Object.assign to set default value. The output is:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3ns017zd9udgwfopc78b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3ns017zd9udgwfopc78b.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another topic added here is ref (&lt;a href="https://reactjs.org/docs/forwarding-refs.html" rel="noopener noreferrer"&gt;https://reactjs.org/docs/forwarding-refs.html&lt;/a&gt;). Here is a TypeScript sample to deal with it:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import React, { FormEvent } from "react"

/**
 * API form properties
 */
export interface APIFormProps {
    /**
     * Style class name
     */
    className?: string
}

/**
 * API form reference interface
 */
export interface APIFormRef {
    changeHandler(event: React.ChangeEvent&amp;lt;HTMLInputElement&amp;gt;):void
}

/**
 * API Form
 * @param props 
 * @param ref 
 */
const APIFormForward : React.ForwardRefRenderFunction&amp;lt;APIFormRef, React.PropsWithChildren&amp;lt;APIFormProps&amp;gt;&amp;gt; = (
    props,
    ref
) =&amp;gt; {
    // hooks
    const formElement = React.useRef&amp;lt;HTMLFormElement | null&amp;gt;(null);
    React.useImperativeHandle(ref, () =&amp;gt; ({
        changeHandler: (event: React.ChangeEvent&amp;lt;HTMLInputElement&amp;gt;) =&amp;gt; {
            console.log(event)
        }
      }))

    return (
        &amp;lt;form ref={formElement} {...props}&amp;gt;{props.children}&amp;lt;/form&amp;gt;
    )
}
export const APIForm = React.forwardRef(APIFormForward)

    // Form reference
    let formRef = React.useRef&amp;lt;APIFormRef&amp;gt;(null)

    const changeHandler = (event: React.ChangeEvent&amp;lt;HTMLInputElement&amp;gt;) =&amp;gt; {
        // Call the method from an event handler
        formRef.current?.changeHandler(event)
    }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;By the way, &lt;a href="https://codesandbox.io/" rel="noopener noreferrer"&gt;https://codesandbox.io/&lt;/a&gt; is a good place for practicing. That's all. Enjoy yourself on the journey!&lt;/p&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>materialui</category>
    </item>
    <item>
      <title>Initial points of a React project</title>
      <dc:creator>Garry Xiao</dc:creator>
      <pubDate>Mon, 27 Apr 2020 08:51:12 +0000</pubDate>
      <link>https://forem.com/garryxiao/initial-points-of-a-react-project-2o10</link>
      <guid>https://forem.com/garryxiao/initial-points-of-a-react-project-2o10</guid>
      <description>&lt;p&gt;React is popular, with the understanding of its methodology, take it like Lego, all are components, build it with components. I prefer the functional components to class-based components. Writing functional components with TypeScript is a necessary combination nowadays. Except that, there are 4 points we need to determine at the elementary stage.&lt;/p&gt;

&lt;p&gt;A. Global state management, except 'useState' for the local state of functional components and 'state' property of class-based components. Two solutions:&lt;br&gt;
a) Context, designed to share data that can be considered “global” for a tree of React components. Apply it sparingly because it makes component reuse more difficult (&lt;a href="https://reactjs.org/docs/context.html"&gt;https://reactjs.org/docs/context.html&lt;/a&gt;). It's better to use different Contexts for different purposes. Here I create two Contexts, UserStateContext/UserStateProvider for user state, LanguageStateContext/LanguageStateProvider  for language and labels state.&lt;/p&gt;

&lt;p&gt;In order to improve the code reuse, create a generic function to create them.&lt;br&gt;
CreateState.tsx source codes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from "react"
import { IState, IAction, IUpdate } from "./IState"

/**
 * Generic to create state context and provider
 * @param reducer Reduce function
 * @param initState Init state
 */
export function CreateState&amp;lt;S extends IState, A extends IAction&amp;gt;(reducer: React.Reducer&amp;lt;S, A&amp;gt;, initState: S) {
    // State context
    const context = React.createContext({} as IUpdate&amp;lt;S, A&amp;gt;)

    // State context provider
    const provider: React.FunctionComponent = (props) =&amp;gt; {
        // Update reducer
        const [state, dispatch] = React.useReducer(reducer, initState)

        // Avoid unnecessary re-renders
        // https://alligator.io/react/usememo/
        const contextValue = React.useMemo(() =&amp;gt; {
            return { state, dispatch }
        }, [state, dispatch])

        return (
            &amp;lt;context.Provider value={contextValue}&amp;gt;{props.children}&amp;lt;/context.Provider&amp;gt;
        )
    }

    // Return
    return {
        context,
        provider
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;IState.ts to define needed interfaces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from "react"

/**
 * State data interface
 */
export interface IState {
}

/**
 * State action interface
 */
export interface IAction {
}

/**
 * State update interface
 */
export interface IUpdate&amp;lt;S extends IState, A extends IAction&amp;gt; {
    state: S,
    dispatch: React.Dispatch&amp;lt;A&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;LanguageState.ts source code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { IAction } from "./IState"
import { CreateState } from "./CreateState"

/**
 * Language label
 * Indexable type
 */
export interface LanguageLabel {
    [key: string]: string
}

/**
 * Language state
 */
export interface ILanguage {
    /**
     * Global labels
     */
    labels: LanguageLabel

    /**
     * Current language name
     */
    name: string
}

/**
 * Language action to manage language and labels
 */
export interface LanguageAction extends IAction {
    /**
     * Language cid, like 'zh-CN'
     */
    name: string,

    /**
     * Labels of the language
     */
    labels: LanguageLabel
}

/**
 * Language reducer
 * @param state State
 * @param action Action
 */
export function LanguageReducer(state: ILanguage, action: LanguageAction) {
    if(action?.name) {
        return Object.assign({}, state, action)
    }

    return state
}

/**
 * Language context and provider
 */
export const { context: LanguageStateContext, provider: LanguageStateProvider } = CreateState(LanguageReducer, {} as ILanguage)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;UserState.ts source codes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { IAction } from "./IState"
import { CreateState } from "./CreateState"

/**
 * Application user update interface
 */
export interface IUserUpdate {
    /**
     * User name
     */
    name: string
}

/**
 * Application user interface
 */
export interface IUser extends IUserUpdate {
    /**
     * Authorized or not
     */
    authorized: boolean

    /**
     * User id
     */
    id: number

    /**
     * Organization current user belongs
     */
    organization_id: number
}

/**
 * User action type
 * Style like 'const enum' will remove definition of the enum and cause module errors
 */
export enum UserActionType {
    // Login action
    Login = "LOGIN",

    // Logout action
    Logout = "LOGOUT",

    // Update action
    Update = "UPDATE"
}

/**
 * User action to manage the user
 */
export interface UserAction extends IAction {
    /**
     * Type
     */
    type: UserActionType,

    /**
     * User
     */
    user?: IUser,

    /**
     * User update
     */
    update?: IUserUpdate
}

/**
 * User reducer
 * @param state State
 * @param action Action
 */
export function UserReducer(state: IUser, action: UserAction) {
    switch(action.type) {
        case UserActionType.Login:
            return Object.assign({}, action.user)
        case UserActionType.Logout:
            return {} as IUser
        case UserActionType.Update:
            return Object.assign({}, state, action.update)
        default:
            return state
    }
}

/**
 * User context and provider
 */
export const { context: UserStateContext, provider: UserStateProvider } = CreateState(UserReducer, {} as IUser)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;index.tsx source code to show how to use the Contexts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react'
import ReactDOM from 'react-dom'
import { UserStateProvider, LanguageStateProvider } from 'etsoo-react'
import './index.css'
import App from './App'
import * as serviceWorker from './serviceWorker'

ReactDOM.render(
  &amp;lt;React.StrictMode&amp;gt;
    &amp;lt;UserStateProvider&amp;gt;
      &amp;lt;LanguageStateProvider&amp;gt;
        &amp;lt;App /&amp;gt;
      &amp;lt;/LanguageStateProvider&amp;gt;
    &amp;lt;/UserStateProvider&amp;gt;
  &amp;lt;/React.StrictMode&amp;gt;,
  document.getElementById('root')
)

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;b) Redux (&lt;a href="https://redux.js.org/"&gt;https://redux.js.org/&lt;/a&gt;). By contrast with the Context solution, I would like to say, Redux is a little complex and if there is no legacy burden, just give it up.&lt;/p&gt;

&lt;p&gt;B. Router, route-based code-splitting, ideal for Single Page Application (or SPA) (&lt;a href="https://reacttraining.com/react-router/web/guides/quick-start"&gt;https://reacttraining.com/react-router/web/guides/quick-start&lt;/a&gt;).&lt;br&gt;
a) Install: "npm install -g react-router-dom".&lt;br&gt;
b) Install: "npm install -g @types/react-router-dom" for TypeScript support.&lt;br&gt;
c) Use 'Switch', render the first child from top to bottom that matches the path.&lt;br&gt;
d) Add property 'exact' to 'Route' for an exact match, especially to home with '/'.&lt;br&gt;
e) Add private routes to limit page access with authentication.&lt;/p&gt;

&lt;p&gt;Custom PrivateRoute under package 'etsoo-react' codes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react'
import { Redirect, Route, RouteProps } from 'react-router-dom'
import History from 'history'

/**
 * Private router property interface
 */
export interface PrivateRouteProp extends RouteProps {
    authorized: boolean
}

/**
 * Private router redirect state interface
 */
export interface PrivateRouteRedirectState {
    /**
     * referrer location
     */
    referrer: History.Location
}

/**
 * Private route for react-router-dom
 * Configue a strict route with '/login' to redirect to application's actual login page
 * In login page, useLocation() to get the Location object, and Location.state as PrivateRouteRedirectState to access the referrer
 */
export const PrivateRoute:React.FunctionComponent&amp;lt;PrivateRouteProp&amp;gt; = ({authorized, ...rest}) =&amp;gt; {
    return (
        authorized ? &amp;lt;Route {...rest}/&amp;gt; : &amp;lt;Redirect to={{pathname: '/login', state: {referrer: rest.location} as PrivateRouteRedirectState}}/&amp;gt;
    )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;App.tsx source codes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useContext } from 'react'
import { Route, Switch, BrowserRouter } from 'react-router-dom'
import { PrivateRoute, UserStateContext } from 'etsoo-react'

import CustomerSearch from './customer/Search'
import CustomerView from './customer/View'
import Login from './public/Login'
import Main from './main/Main'

function App() {
  // State user
  const { state } = useContext(UserStateContext)

  // Authorized
  const authorized:boolean = (state == null ? false : state.authorized)

  return (
    &amp;lt;BrowserRouter&amp;gt;
      &amp;lt;Switch&amp;gt;
        &amp;lt;PrivateRoute authorized={authorized} path="/customer/search/:condition?" component={CustomerSearch} /&amp;gt;
        &amp;lt;PrivateRoute authorized={authorized} path="/customer/view/:id" component={CustomerView} /&amp;gt;
        &amp;lt;PrivateRoute authorized={authorized} exact path="/main" component={Main} /&amp;gt;
        &amp;lt;Route component={Login} /&amp;gt;
      &amp;lt;/Switch&amp;gt;
    &amp;lt;/BrowserRouter&amp;gt;
  )
}

export default App
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the login page, example codes to show how to access it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react'
import { useLocation } from 'react-router-dom'
import { PrivateRouteRedirectState } from 'etsoo-react'

function Login() {
    let location = useLocation()
    let state = location.state as PrivateRouteRedirectState

    return (
    &amp;lt;h1&amp;gt;Login page, referer: { state?.referrer?.pathname }&amp;lt;/h1&amp;gt;
    )
}

export default Login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;C. API consuming, apply 'npm install -g axios' to install Axios, or any other framework you are familiar with. It's not the key issue. As API consuming, work together with the API developer with mutual understandings of each other of standards and rules. Then in React, encapsulation is the key part. Consuming APIs in page-level components, not component parts under it, will make sure the components reuse not interrupted by API calls.&lt;/p&gt;

&lt;p&gt;Thanks to TypeScript, make JavaScript programming much checkable and reasonable. For example, there is an API to get a list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    /**
     * Get tiplist data
     * @param model Data model
     */
    async tiplist&amp;lt;M extends TiplistModel&amp;gt;(model: M | null = null) {
        return (await this.api.get('tiplist', { params: model })).data as IListItem[]
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Parameters are abstracted to a model for client calls. There is also a very similar model definition on the server-side to receive and hold the parameters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
 * Tiplist model
 */
export interface TiplistModel {
    /**
     * Id
     */
    id: number,

    /**
     * Id array
     */
    ids: number[],

    /**
     * Hide id
     */
    hideId: number,

    /**
     * Hide ids array
     */
    hideIds: number[],

    /**
     * Records to read
     */
    records: number

    /**
     * Search keyword
     */
    sc: string
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result is also cast to an interface IListIItem array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
 * Common list item interface
 */
export interface IListItem {
    // Id
    id: number

    // Label
    label: string
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could leave the results as pure JSON. But strong types are definitely useful even for a small project, for coding, debugging, and testing.&lt;/p&gt;

&lt;p&gt;D. Testing&lt;br&gt;
&lt;a href="https://reactjs.org/docs/testing-recipes.html"&gt;https://reactjs.org/docs/testing-recipes.html&lt;/a&gt;. As a team leader and project manager, For small and medium-size projects, I prefer to put the testing responsibility on coding engineers first, borrow some ideas with TDD, during the development step to speed up the output. After the project launched and somehow stable, then merge all testing codes into a unique role.&lt;/p&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Build a React components NPM package and CI/CD with Github Action</title>
      <dc:creator>Garry Xiao</dc:creator>
      <pubDate>Fri, 10 Apr 2020 22:04:06 +0000</pubDate>
      <link>https://forem.com/garryxiao/build-a-react-components-npm-package-and-ci-cd-with-github-action-1jm6</link>
      <guid>https://forem.com/garryxiao/build-a-react-components-npm-package-and-ci-cd-with-github-action-1jm6</guid>
      <description>&lt;p&gt;Lockdown in NZ creates spare time for me to enjoy something occupied before with chore. Recently, I started to summarize the React framework of SmartERP that a SaaS service I led before, try some new solutions. I would like to demonstrate how to build an NPM (Node.js Package Manager) package and implement CI/CD with Github Action to automate most of the work.&lt;/p&gt;

&lt;p&gt;Preparation&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new public repository "ETSOO/etsoo-react" on GitHub.&lt;/li&gt;
&lt;li&gt;Change to the target folder, clone the repository into it: "git clone &lt;a href="https://github.com/ETSOO/etsoo-react"&gt;https://github.com/ETSOO/etsoo-react&lt;/a&gt;".&lt;/li&gt;
&lt;li&gt;Install the latest Node.js to your computer(&lt;a href="https://nodejs.org/en/"&gt;https://nodejs.org/en/&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Run 'Node.js command prompt', input command 'npm init' to initialize an NPM package. 'npm init -y' will not ask any question and produce the package.json file with default values. 'npm init --scope=&lt;code&gt;&amp;lt;your_org_name&amp;gt;&lt;/code&gt;' to create an Org scoped package.&lt;/li&gt;
&lt;li&gt;'git add package.json', multiple files are separated by a blank (Boring? learn something from &lt;a href="https://stackabuse.com/git-add-all-files-to-a-repo/"&gt;https://stackabuse.com/git-add-all-files-to-a-repo/&lt;/a&gt;, 'git add -A'), then 'git commit -m "Message about the commit"' make changes to the local depository, then 'git push origin master' to upload changes to Github. If you made changes on Github, you need to first pull the updates 'git pull origin master' and then push the local version. Beware of any conflicts here. Run 'git stash git pull git stash pop' to keep local updates.&lt;/li&gt;
&lt;li&gt;Create an account in the NPM registry (&lt;a href="https://www.npmjs.com/signup"&gt;https://www.npmjs.com/signup&lt;/a&gt;). Enter the command 'npm login', provide account details you just have to complete it. Enter the command 'npm publish' to publish, every time needs to upgrade the "version" under package.json.&lt;/li&gt;
&lt;li&gt;IDE(Integrated Development Environment): Visual Studio Code, &lt;a href="https://code.visualstudio.com/"&gt;https://code.visualstudio.com/&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;React &amp;amp; TypeScript:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;'npm install -g typescript react react-dom @types/react @types/react-dom' install the minimal set of dependencies required to React and TypeScript.&lt;/li&gt;
&lt;li&gt;Create a folder 'src', create an 'index.ts' under it. Change package.json, set "main": "lib/index.js" (It's not so perfect to include the ts files directly, with "src/index.ts" will cause parse error, seems tsc will not compile files under node_modules); "types": "lib/index.d.ts", scripts add "build": "tsc".&lt;/li&gt;
&lt;li&gt;Copy a 'tsconfig.json' from another project to the root and change settings as you want or 'npx tsc --init'. Set "declaration": true, generates corresponding definitions in index.d.ts. "jsx": "react" if include 'tsx' files. "outDir": "./lib" tell the compiler that the 'src' folder will be compiled to javascript in 'lib' folder. Add the folder name to '.gitignore'. Make sure "noEmit": false.&lt;/li&gt;
&lt;li&gt;If unknown errors occurred, run 'npm install' to check the dependencies and install any missing packages.&lt;/li&gt;
&lt;li&gt;Test your new NPM module without publishing it (&lt;a href="https://medium.com/@the1mills/how-to-test-your-npm-module-without-publishing-it-every-5-minutes-1c4cb4b369be"&gt;https://medium.com/@the1mills/how-to-test-your-npm-module-without-publishing-it-every-5-minutes-1c4cb4b369be&lt;/a&gt;). Run 'npm link' to define a global link. Then go to the project share this package, run 'npm link etsoo-react' to add the global link to the 'node_modules' like it has been published. Run '' to remove the link. Run 'npm unlink --no-save ' on your project’s directory to remove the local symlink, and run 'npm unlink' on the module’s directory to remove the global symlink. (26/05/2021 update: just install local package with: npm install file:../packagename and the link will be created automatically)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Testings:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run 'npm i jest @types/jest ts-jest -D' to install the testing framework Jest (&lt;a href="https://jestjs.io/"&gt;https://jestjs.io/&lt;/a&gt;). Add a folder 'tests' under the root, add a test script file to pass the 'npm test' command.&lt;/li&gt;
&lt;li&gt;Add "jest": "^25.3.0", under package.json/devDependencies if not exits and run 'npm install'.&lt;/li&gt;
&lt;li&gt;add   "jest": {
"testMatch": [ "/tests/*&lt;em&gt;/&lt;/em&gt;.js" ]
}, to the package.json. Limit Jtest to the folder "&lt;strong&gt;test&lt;/strong&gt;" under root.&lt;/li&gt;
&lt;li&gt;Install vscode-jest&lt;/li&gt;
&lt;li&gt;npm install -D react-test-renderer&lt;/li&gt;
&lt;li&gt;npm install -D babel-jest babel-core@^7.0.0-bridge.0 &lt;a class="mentioned-user" href="https://dev.to/babel"&gt;@babel&lt;/a&gt;
/core &lt;a class="mentioned-user" href="https://dev.to/babel"&gt;@babel&lt;/a&gt;
/preset-env regenerator-runtime (&lt;a href="https://jestjs.io/docs/en/22.x/getting-started.html"&gt;https://jestjs.io/docs/en/22.x/getting-started.html&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;npm install --save-dev enzyme enzyme-adapter-react-16 @types/enzyme @types/enzyme-adapter-react-16&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;tsconfig.json:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "compilerOptions": {
    "outDir": "./lib",
    "target": "ES2018",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": false,
    "jsx": "react",
    "declaration": true
  },
  "include": [
    "src"
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;package.json:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "name": "etsoo-react",
  "version": "1.0.2",
  "description": "ETSOO React TypeScript components NPM package",
  "main": "lib/index.js",
  "types": "lib/index.d.ts",
  "scripts": {
    "build": "tsc",
    "test": "jest"
  },
  "jest": {
    "testMatch": [ "&amp;lt;rootDir&amp;gt;/tests/**/*.js" ]
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/ETSOO/etsoo-react.git"
  },
  "author": "Garry Xiao",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/ETSOO/etsoo-react/issues"
  },
  "homepage": "https://github.com/ETSOO/etsoo-react#readme",
  "devDependencies": {
    "@types/react": "^16.9.33",
    "@types/react-dom": "^16.9.6",
    "jest": "^25.3.0",
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "typescript": "^3.8.3"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Github Actions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Choose 'Publish Node.js Package' from 'Popular continuous integration workflows' under the Actions tab and create the template YAML file under 'etsoo-react/.github/workflows/'. View &lt;a href="https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#about-yaml-syntax-for-workflows"&gt;https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#about-yaml-syntax-for-workflows&lt;/a&gt; for help.&lt;/li&gt;
&lt;li&gt;Log in NPM, under 'Auth Tokens', create a new one and copy the token, then go to Github repository's settings, secrets, 'add a new secret' named 'GXNpmToken' here.&lt;/li&gt;
&lt;li&gt;Github desktop (&lt;a href="https://desktop.github.com/"&gt;https://desktop.github.com/&lt;/a&gt;) is an interesting tool to help you manage depositories.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;YAML file content, please view &lt;a href="https://github.com/ETSOO/etsoo-react/blob/master/.github/workflows/npmpublish.yml"&gt;https://github.com/ETSOO/etsoo-react/blob/master/.github/workflows/npmpublish.yml&lt;/a&gt;. After you push the changes, the Action will execute and later you will receive a notification email from NPM. That's really exciting!&lt;/p&gt;

&lt;p&gt;(2020/7/23) Setting up ESLint to work with TypeScript (&lt;a href="https://www.robertcooper.me/using-eslint-and-prettier-in-a-typescript-project"&gt;https://www.robertcooper.me/using-eslint-and-prettier-in-a-typescript-project&lt;/a&gt;)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;npm install eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin -D.&lt;/li&gt;
&lt;li&gt;npx eslint --init, choose a popular styling like airbnb, json format.&lt;/li&gt;
&lt;li&gt;"comma-dangle": ["error", "never"], "no-console": "off", "arrow-parens": "off", "linebreak-style": "off".&lt;/li&gt;
&lt;li&gt;npm install prettier -D. Install prettier extension in VSCode. "File" -&amp;gt; "References" -&amp;gt; "Settings" -&amp;gt; search "Format On Save".&lt;/li&gt;
&lt;li&gt;npm install -D eslint-config-prettier. Create a local configure file '.prettierrc'.
Example project: &lt;a href="https://github.com/ETSOO/restclient"&gt;https://github.com/ETSOO/restclient&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you want to upgrade all dependencies, should be very careful, please follow: &lt;a href="https://flaviocopes.com/update-npm-dependencies/"&gt;https://flaviocopes.com/update-npm-dependencies/&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;npm outdated, give a list.&lt;/li&gt;
&lt;li&gt;npm install -g npm-check-updates, install the tool.&lt;/li&gt;
&lt;li&gt;ncu -u, update the version.&lt;/li&gt;
&lt;li&gt;npm update / npm install&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is the help link about how to create a template repository and how to use it: &lt;a href="https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-template-repository"&gt;https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-template-repository&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>npm</category>
      <category>github</category>
      <category>action</category>
    </item>
    <item>
      <title>Web vulnerabilities and options for .net core API 3.1</title>
      <dc:creator>Garry Xiao</dc:creator>
      <pubDate>Wed, 08 Apr 2020 12:04:22 +0000</pubDate>
      <link>https://forem.com/garryxiao/web-vulnerabilities-and-options-for-net-core-api-3-1-1ebf</link>
      <guid>https://forem.com/garryxiao/web-vulnerabilities-and-options-for-net-core-api-3-1-1ebf</guid>
      <description>&lt;p&gt;There are four common vulnerabilities in web applications. Be aware of these risks, master features of the technology stacks that help you secure your apps and prevent security breaches is necessary.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Cross-site scripting attacks (XSS). Core tip: All data received from clients are untrusted. When you want to output the content, keep an eye on any possibility of including any executable scripts.&lt;br&gt;
&lt;a href="https://en.wikipedia.org/wiki/Cross-site_scripting"&gt;https://en.wikipedia.org/wiki/Cross-site_scripting&lt;/a&gt;&lt;br&gt;
&lt;a href="https://owasp.org/www-community/attacks/xss/"&gt;https://owasp.org/www-community/attacks/xss/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.microsoft.com/en-us/aspnet/core/security/cross-site-scripting?view=aspnetcore-3.1"&gt;https://docs.microsoft.com/en-us/aspnet/core/security/cross-site-scripting?view=aspnetcore-3.1&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SQL injection attacks. Core tip: The concatenation of raw SQL command text with parameters or parts from an untrusted source should be seriously validated.&lt;br&gt;
&lt;a href="https://en.wikipedia.org/wiki/SQL_injection"&gt;https://en.wikipedia.org/wiki/SQL_injection&lt;/a&gt;&lt;br&gt;
&lt;a href="https://portswigger.net/web-security/sql-injection"&gt;https://portswigger.net/web-security/sql-injection&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.microsoft.com/en-us/ef/core/querying/raw-sql"&gt;https://docs.microsoft.com/en-us/ef/core/querying/raw-sql&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cross-Site Request Forgery (CSRF), also known as one-click attack or session riding. Core tip: Two websites are browsed, one log in and another is malicious. Submit requests from the malicious website attached with your valid cookie authentication is the common way to attack.&lt;br&gt;
&lt;a href="https://en.wikipedia.org/wiki/Cross-site_request_forgery"&gt;https://en.wikipedia.org/wiki/Cross-site_request_forgery&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.microsoft.com/en-us/aspnet/core/security/anti-request-forgery?view=aspnetcore-3.1"&gt;https://docs.microsoft.com/en-us/aspnet/core/security/anti-request-forgery?view=aspnetcore-3.1&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open redirect attacks, also known as "Unvalidated Redirects and Forwards". Core tip: If the login redirects with an unchecked query parameter, users from a fake link could be redirected to a similar login page and causing their credential data leaks.&lt;br&gt;
&lt;a href="https://www.trustwave.com/en-us/resources/blogs/spiderlabs-blog/understanding-and-discovering-open-redirect-vulnerabilities/"&gt;https://www.trustwave.com/en-us/resources/blogs/spiderlabs-blog/understanding-and-discovering-open-redirect-vulnerabilities/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.microsoft.com/en-us/aspnet/core/security/preventing-open-redirects?view=aspnetcore-3.1"&gt;https://docs.microsoft.com/en-us/aspnet/core/security/preventing-open-redirects?view=aspnetcore-3.1&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;.Net core web API 3.1 is the latest framework of Microsoft to develop REST API.&lt;br&gt;
Use cookie authentication without ASP.NET Core Identity (&lt;a href="https://docs.microsoft.com/en-us/aspnet/core/security/authentication/cookie?view=aspnetcore-3.1"&gt;https://docs.microsoft.com/en-us/aspnet/core/security/authentication/cookie?view=aspnetcore-3.1&lt;/a&gt;.) is easy and quick. HttpOnly cookies will be used by default. Httponly flag is very important to avoid any XSS attack and has other benefits (&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies"&gt;https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies&lt;/a&gt;). Cookie solution relies on client's cookie support.&lt;/p&gt;

&lt;p&gt;Another common authentication solution for API is to use JWT (JSON Web Token, &lt;a href="https://jwt.io"&gt;https://jwt.io&lt;/a&gt;). &lt;a href="https://jasonwatmore.com/post/2019/10/11/aspnet-core-3-jwt-authentication-tutorial-with-example-api"&gt;https://jasonwatmore.com/post/2019/10/11/aspnet-core-3-jwt-authentication-tutorial-with-example-api&lt;/a&gt;. In start.cs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            if (Settings.Cors?.Length &amp;gt; 0)
            {
                services.AddCors(options =&amp;gt;
                {
                    options.AddPolicy("platform",
                    builder =&amp;gt;
                    {
                        builder.WithOrigins(Settings.Cors)
                            // Support https://*.domain.com
                            .SetIsOriginAllowedToAllowWildcardSubdomains()

                            // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
                            // https://stackoverflow.com/questions/24687313/what-exactly-does-the-access-control-allow-credentials-header-do
                            // JWT is not a cookie solution, disable it without allow credential
                            // .AllowCredentials()
                            .DisallowCredentials()

                            // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
                            // Without it will popup error: Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response
                            .AllowAnyHeader()

                            // Web Verbs like GET, POST, default enabled
                            .AllowAnyMethod();
                    });
                });
            }

            // Configue compression
            // https://gunnarpeipman.com/aspnet-core-compress-gzip-brotli-content-encoding/
            services.Configure&amp;lt;BrotliCompressionProviderOptions&amp;gt;(options =&amp;gt;
            {
                options.Level = CompressionLevel.Optimal;
            });

            services.AddResponseCompression(options =&amp;gt;
            {
                options.EnableForHttps = true;
                options.Providers.Add&amp;lt;BrotliCompressionProvider&amp;gt;();
            });

            // Configue JWT authentication
            // https://jwt.io/
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options =&amp;gt;
                {
                    // Is SSL only
                    options.RequireHttpsMetadata = Settings.SSL;

                    // Save token, True means tokens are cached in the server for validation
                    options.SaveToken = false;

                    // Token validation parameters
                    options.TokenValidationParameters = new TokenValidationParameters()
                    {
                        ValidateIssuerSigningKey = true,
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(app.Configuration.SymmetricKey)),
                        ValidateIssuer = false,
                        ValidateAudience = false
                    };
                });
        }
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            // Enable HTTPS redirect
            if (Settings.SSL)
                app.UseHttpsRedirection();

            app.UseRouting();

            // Enable CORS (Cross-Origin Requests)
            // The call to UseCors must be placed after UseRouting, but before UseAuthorization
            if (Settings.Cors?.Length &amp;gt; 0)
            {
                app.UseCors("platform");
            }

            app.UseAuthentication();
            app.UseAuthorization();

            // Enable compression
            app.UseResponseCompression();

            app.UseEndpoints(endpoints =&amp;gt;
            {
                // Apply authentication by default
                endpoints.MapControllers().RequireAuthorization();
            });
        }

After the user log in successfully, add the codes below to generate the token:
        /// &amp;lt;summary&amp;gt;
        /// Login for authentication
        /// 登录授权
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name="model"&amp;gt;Data model&amp;lt;/param&amp;gt;
        /// &amp;lt;returns&amp;gt;Result&amp;lt;/returns&amp;gt;
        [AllowAnonymous]
        [HttpPost("Login")]
        public async Task Login([FromBody]LoginModel model)
        {
            // Act
            var result = await Service.LoginAsync(model);

            if (result.OK)
            {
                // Logined user id
                var userId = result.Data.Get("token_user_id", 0);

                // User role
                var role = result.Data.Get("role", UserRole.User);

                // Token handler
                var tokenHandler = new JwtSecurityTokenHandler();

                // Key bytes
                var key = Encoding.ASCII.GetBytes(App.Configuration.SymmetricKey);

                // Token descriptor
                var tokenDescriptor = new SecurityTokenDescriptor
                {
                    Subject = new ClaimsIdentity(new Claim[]
                    {
                        new Claim(ClaimTypes.Name, userId.ToString()),
                        new Claim(ClaimTypes.Role, role.ToString().ToLower()),
                    }),
                    // Suggest to refresh it at 5 minutes interval, two times to update
                    Expires = DateTime.UtcNow.AddMinutes(12),
                    SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256)
                };

                // Hold the token value and then return to client
                var token = tokenHandler.CreateToken(tokenDescriptor);
                result.Data["authorization"] = tokenHandler.WriteToken(token);
            }

            // Output
            await ResultContentAsync(result);
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the token is stateless, Web APIs are always facing a replay attack, also known as playback attack. bearer.SaveToken = true means you could access it through await HttpContext.GetTokenAsync("access_token") for any outgoing request. Add necessary validation logic in the database side is helpful. A short-lived, strict authentication with rate-limiting policy token solution will make the project much stronger.&lt;/p&gt;

</description>
      <category>security</category>
      <category>dotnet</category>
      <category>webapi</category>
      <category>jwt</category>
    </item>
  </channel>
</rss>
