# .Net with react
## 環境安裝
1. .NET SDK 下載
https://dotnet.microsoft.com/en-us/download

2. node.js 下載
```
brew install node
```
## 創立專案
列出所有.net 的 template
```
dotnet new -l
```

創立.net with react 專案
```
dotnet new react -o new_project
```

ClientApp (以react.js 框架的前端開發)
Program.cs (.net core 第一個入點)
## React 前端開發
### 簡介
React.js 是一個JavaScript庫,用於建立用戶界面(User Interfaces)的前端應用程式。以下是React.js的主要特點和編程風格:
**1. 組件導向:** React.js的核心概念是組件。你可以創建多個可重用的UI組件,然後組合它們來構建整個應用程式。這種組件導向的編程風格使代碼更加模塊化和易於維護。
**2. 虛擬DOM(Virtual DOM):** React使用虛擬DOM來提高性能。它在內部維護一個虛擬版本的DOM,當數據更改時,它比較虛擬DOM和實際DOM,只更新必要的部分,這樣可以減少DOM操作,提高應用程式的效能。
**3. 單向數據流:** React中的數據流是單向的,這意味著數據的變化只能從父組件流向子組件。這種數據流模型使應用程式的狀態管理更加可控。
**4. JSX(JavaScript XML):** React使用JSX語法,這是一種將HTML和JavaScript結合的語法。它使你可以在JavaScript代碼中直接編寫HTML標記,使代碼更容易閱讀和理解。
**5. 高度可定製性:** React組件是高度可定製的,你可以輕鬆地創建自己的組件,樣式它們,並添加自定義邏輯。
### 部件介紹

**src:** 這是應用程式的源代碼文件夾,包含所有React組件、CSS樣式文件、JavaScript代碼和其他應用程式邏輯。
**node_modules:** 這是Node.js包管理系統(通常使用npm或Yarn)生成的文件夾,包含了應用程式所需的所有第三方庫和依賴項。這些庫包括React本身、React Router、Redux等。
**public:** 這個文件夾包含了用戶端應用程序的靜態資源,如HTML文件、圖片、字體等。通常,應用程序的入口點(index.html)也位於這個文件夾中。
因此我們編程的文件夾是src

**App.js:** 這是React應用程序的主要組件,通常包含應用程序的主要佈局和路由配置。它是應用程序的入口點,負責渲染其他組件。
**AppRoutes.js:** 這個文件通常包含React Router的路由配置,定義了應用程序中不同路由的組件和對應的URL路徑。
**components:** 這是一個文件夾,包含了應用程序中的各種React組件,這些組件可以在應用程序的不同部分中重用。組件通常按功能或類別進行組織。
**custom.css:** 這是自定義的CSS樣式文件,用於設置應用程序的外觀和風格。它包含了應用程序的自定義樣式規則。
**index.js:** 這是React應用程序的另一個入口點,負責將應用程序渲染到DOM中的根元素。通常,它導入App.js組件並調用ReactDOM.render來初始化應用程序。
### JSX 語法
JSX使您可以在JavaScript代碼中嵌入HTML標記
創建組件有兩種主要的風格:Class-Based Components vs Functional Components
**Functional**
定義方式: 這種風格的組件是純JavaScript函數,接收屬性(**props**)並返回React元素。
生命週期方法: 在函數組件中,您可以使用React的Hooks,如useEffect,以處理生命週期操作。
狀態(**State**): 使用useState Hook,函數組件可以管理內部狀態。
Example:
```
const [count, setCount] = useState(0);
```
count 是狀態變量
setCount 是一個函數用於當事件觸發時設定變量值(可以為JS 中任何數據類型)
屬性(**Props**): 函數組件可以接收來自父組件的屬性。
父組件:
```
function ParentComponent() {
const dataToPass = "Hello from Parent Component";
return (
<div>
<ChildComponent propName={dataToPass} />
</div>
);
}
```
子組件:
```
function ChildComponent(props) {
// 使用 props 中的数据
const data = props.propName;
return (
<div>
<p>Received data: {data}</p>
</div>
);
}
```
Functional Example :
```
import React, { useState, useEffect } from 'react';
function FunctionalComponent() {
const [count, setCount] = useState(0);
useEffect(() => {
// 在組件渲染後執行初始化操作
}, []);
const handleClick = () => {
setCount(count + 1);
}
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
}
export default FunctionalComponent;
```
**Functional 比 class-base 簡單直觀,盡量用Functional 寫**
### React js Problem solving
#### 1.MUI 和 react 版本不一致
```
null is not an object (evaluating 'dispatcher.useContext')
useContext@https://localhost:44476/static/js/bundle.js:85039:24
Button@https://localhost:44476/static/js/bundle.js:74751:69
renderWithHooks@https://localhost:44476/static/js/bundle.js:30590:31
updateForwardRef@https://localhost:44476/static/js/bundle.js:33161:39
callCallback@https://localhost:44476/static/js/bundle.js:20182:23
dispatchEvent@[native code]
invokeGuardedCallbackDev@https://localhost:44476/static/js/bundle.js:20226:33
invokeGuardedCallback@https://localhost:44476/static/js/bundle.js:20283:40
beginWork$1@https://localhost:44476/static/js/bundle.js:40157:32
performUnitOfWork@https://localhost:44476/static/js/bundle.js:39404:27
workLoopSync@https://localhost:44476/static/js/bundle.js:39327:26
renderRootSync@https://localhost:44476/static/js/bundle.js:39300:23
performConcurrentWorkOnRoot@https://localhost:44476/static/js/bundle.js:38694:92
workLoop@https://localhost:44476/static/js/bundle.js:69360:46
flushWork@https://localhost:44476/static/js/bundle.js:69338:26
performWorkUntilDeadline@https://localhost:44476/static/js/bundle.js:69575:46
```
**解決辦法**
`npm install --legacy-peer-deps`
## .Net core 後端開發
### Database
以下用sqlite 作例子
https://learn.microsoft.com/zh-tw/ef/core/get-started/overview/first-app?tabs=netcore-cli
#### sqlite 介紹
* SQLite 是一種嵌入式資料庫,這表示它將整個資料庫引擎嵌入到應用程式中,無需單獨的資料庫伺服器。
* SQLite 遵循 SQL(Structured Query Language)標準
#### Entity Framework Core(EF Core)
EF Core是一個輕量、跨平台且開源的物件關聯映射(Object-Relational Mapping,ORM)框架
#### 安裝 EF core 套件 (SQLite)
`dotnet add package Microsoft.EntityFrameworkCore.Sqlite`
其他資料庫套件
https://learn.microsoft.com/zh-tw/ef/core/providers/?tabs=dotnet-core-cli
#### 建立模型
```
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public string DbPath { get; }
public BloggingContext(DbContextOptions<BloggingContext> options) : base(options)
{
var folder = Environment.SpecialFolder.LocalApplicationData;
var path = Environment.GetFolderPath(folder);
DbPath = System.IO.Path.Join(path, "blogging.db");
}
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlite($"Data Source={DbPath}");
}
```
**DbContext** 是 EF Core 中的 class
上述例子 **BloggingContext** 繼承 **DbContext**
`public DbSet<Blog> Blogs { get; set; }`
**DbSet** 是 DbContext 的屬性
**DbSet<Blog>** 表示 Blogs 屬性對應到資料庫中包含 Blog 實體的表格
```
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; } = new();
}
```
```
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
```
#### 串接Database
1. 安裝套件
```
dotnet tool install --global dotnet-ef //Entity Framework Core的命令列工具,它用於管理資料庫遷移
dotnet add package Microsoft.EntityFrameworkCore.Design //套件包含了設計時工具,用於建立資料庫遷移
```
2. Migrations資料庫遷移
```
dotnet ef migrations add InitialCreate //建立一個新的資料庫遷移
dotnet ef database update //將先前建立的遷移應用到資料庫,以更新資料庫結構
```
3. Program.cs 將資料庫註冊到應用程式中
```
using Microsoft.EntityFrameworkCore;
using auctionWeb_react.Models;
builder.Services.AddDbContext<AuctionDb>(options =>
{
options.UseSqlite("Data Source=auction_database.db");
});
```
4.
### API Setting
#### setupProxy.js
可以幫助前端應用程式找到後端 API 主機,,將前端應用程式的 API 請求路由到正確的後端主機,以確保前端應用程式可以訪問後端資源。
```
const { createProxyMiddleware } = require('http-proxy-middleware');
const { env } = require('process');
const target = env.ASPNETCORE_HTTPS_PORT ? `https://localhost:${env.ASPNETCORE_HTTPS_PORT}` :
env.ASPNETCORE_URLS ? env.ASPNETCORE_URLS.split(';')[0] : 'http://localhost:10839';
const onError = (err, req, resp, target) => {
console.error(`${err.message}`);
}
module.exports = function (app) {
const appProxy = createProxyMiddleware('/api', {
proxyTimeout: 10000,
target: target,
onError: onError,
secure: false,
headers: {
Connection: 'Keep-Alive'
}
});
app.use(appProxy);
};
```
createProxyMiddleware 函數用於創建代理中間件,將以 /api 開頭的所有請求代理到目標主機。
#### API 建立
API 會在Controllers 中建立
HelloWorldController.cs
```
using Microsoft.AspNetCore.Mvc;
namespace auctionWeb_react.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class HelloWorldController : Controller
{
[HttpGet]
public string Get()
{
return "Hello World";
}
}
}
```
裝飾器 `[Route("api/[controller]")]`
[controller] 允許您根據控制器的名稱生成路由,如HelloWorldController -> 'api/helloworld'
[HttpGet] 標記用於指示該方法應該由 HTTP GET 請求處理。
[HttpPost] 標記用於指示該方法應該由 HTTP POST 請求處理。
[HttpPut] 標記用於指示該方法應該由 HTTP PUT 請求處理。
[HttpDelete] 標記用於指示該方法應該由 HTTP DELETE 請求處理
#### fetch api
在前端js中調用 fetch api 可以把後端和前端的通訊
fetch get example
```
const apiUrl = "/api/register";
fetch(apiUrl)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.text();
})
.then(data => {
alert(data);
})
.catch(error => {
alert('錯誤: ' + error);
});
```