---
# System prepended metadata

title: .Net with react

---

# .Net with react
## 環境安裝

1. .NET SDK 下載
https://dotnet.microsoft.com/en-us/download
![](https://hackmd.io/_uploads/r1BZ50mz6.png)

2. node.js 下載
```
brew install node
```
## 創立專案
列出所有.net 的 template
```
dotnet new -l
```
![](https://hackmd.io/_uploads/HkWkiA7G6.png)
創立.net with react 專案
```
dotnet new react -o new_project
```
![](https://hackmd.io/_uploads/Sy1csCQM6.png)
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組件是高度可定製的，你可以輕鬆地創建自己的組件，樣式它們，並添加自定義邏輯。
###  部件介紹

![](https://hackmd.io/_uploads/HJYU6CmM6.png)

**src：** 這是應用程式的源代碼文件夾，包含所有React組件、CSS樣式文件、JavaScript代碼和其他應用程式邏輯。

**node_modules：** 這是Node.js包管理系統（通常使用npm或Yarn）生成的文件夾，包含了應用程式所需的所有第三方庫和依賴項。這些庫包括React本身、React Router、Redux等。

**public：** 這個文件夾包含了用戶端應用程序的靜態資源，如HTML文件、圖片、字體等。通常，應用程序的入口點（index.html）也位於這個文件夾中。

因此我們編程的文件夾是src
![](https://hackmd.io/_uploads/SkOaRCQza.png)
**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);  
        });
```
    