# .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); }); ```