# React 是什麼?
React 是由 Facebook 在 2013 年開源的 **用戶介面(UI)函式庫**,專注在 MVC/MVVM 架構中的 **View(V)**。
* **輕量聚焦**:只負責畫面層,不包山包海,其他需求(狀態管理、路由、資料抓取)多仰賴生態系套件搭配。這也讓它 **可組合、可替換**,靈活度高。
* **設計哲學**:*Learn Once, Write Anywhere*──學會一次概念,可在 Web、行動(React Native)等多種平台應用。
* **React Native**([https://reactnative.dev/](https://reactnative.dev/)):以 React 思維開發近原生的 iOS / Android App;樣式語法類似 CSS;底層以橋接方式渲染到原生元件。
> 小提醒:React 並不是 MVVM 的 ViewModel,它更像是「把 UI 拆成一塊塊元件,**以資料驅動畫面**」的思維工具。
<br/>
## 核心特點
1. **宣告式 UI(Declarative)**
你描述「狀態如何 → 畫面就會長怎樣」。這讓程式可讀性高,行為可預測,也更容易除錯與測試。
2. **組件化(Componentization)**
把 UI 拆成可重用的小元件(Button、Card、ListItem…),**各自單純、好維護**。元件可組合成更大的區塊,像堆積木一樣蓋出整個應用。
3. **單向資料流(One-way Data Flow)**
資料由父元件透過 `props` 往子元件流動;子元件不能直接改 `props`,而是透過回呼把意圖(事件)往上拋,讓父元件決定如何更新狀態。
4. **虛擬 DOM(Virtual DOM)**
React 先在記憶體中建一棵虛擬樹(Virtual DOM),狀態變更時用**差異比對**(diffing)計算「最小必要更新」,再一次套到真實 DOM,通常能帶來良好表現與更簡潔的心智模型。

<br/>
## 基本概念
* **JSX**:JavaScript 的語法擴充,讓你可以在 JS 內直接寫近似 HTML 的標記,來描述 UI 結構。
* **元件(Component)**:可用 Class 或 Function 寫成。接受輸入 `props`,回傳要渲染的 React 元素。
* **狀態(State)與屬性(Props)**:
* `state`:元件內部、可變的資料(會驅動畫面重新渲染)。
* `props`:外部傳入、不可在子元件內直接修改的資料。
* **生命週期 / Hooks**:Class 元件有對應的生命週期方法;函式元件透過 **Hooks**(如 `useEffect`)達成掛載、更新、卸載時機的副作用處理。
<br/>
## 為什麼需要 JSX?
**JSX(JavaScript XML)** 不是必須,但非常好用:
* **直觀**:把 UI 結構以「看得見的樹」寫出來。
* **高效率**:標記與邏輯同檔共流,讀寫切換快。
* **工具友善**:型別提示、語法高亮、Lint 與錯誤早期發現。
* **與虛擬 DOM 契合**:JSX 會被轉譯成 `React.createElement(...)` 呼叫,產生虛擬 DOM。
> 注意:瀏覽器不懂 JSX,需用 **Babel** 等工具轉譯成標準 JavaScript 才能執行。
---
## 範例:不用 JSX(原生 API) vs. 使用 JSX
**不使用 JSX(等價寫法)**
```jsx
class HelloMessage extends React.Component {
render() {
return React.createElement('div', null, 'Hello ', this.props.name);
}
}
ReactDOM.render(
React.createElement(HelloMessage, { name: 'Taylor' }),
document.getElementById('hello-example')
);
```
**使用 JSX**
```jsx
class HelloMessage extends React.Component {
render() {
return <div>Hello {this.props.name}</div>;
}
}
ReactDOM.render(
<HelloMessage name="Taylor" />, // 單向資料流的傳入
document.getElementById('hello-example')
);
```
<br/>
## 不經過編譯,直接在瀏覽器使用 React
在學習或做小玩具時,你可以**不設定打包工具**,直接以 `<script>` 引入 UMD 版本:
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>React Without Build Tools</title>
</head>
<body>
<div id="root"></div>
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<script>
'use strict';
class HelloMessage extends React.Component {
render() {
return React.createElement('div', null, `Hello ${this.props.name}`);
}
}
const root = document.querySelector('#root');
ReactDOM.render(React.createElement(HelloMessage, { name: 'React' }), root);
</script>
</body>
</html>
```
若想在瀏覽器**直接寫 JSX**,可再引入 **Babel Standalone**,並把腳本的 `type` 設為 `text/babel`:
```html
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
'use strict';
class HelloMessage extends React.Component {
render() {
return <div>Hello {this.props.name}</div>;
}
}
const root = document.querySelector('#root');
ReactDOM.render(<HelloMessage name="React" />, root);
</script>
```
> **實務建議**:UMD / Babel Standalone 很適合入門或簡報 Demo,但**不建議用於生產**(沒有模組化、HMR、Tree Shaking…)。開發正式專案請使用 Vite / Next.js / CRA(舊)等工具鏈。
<br/>
## 函式元件(Function Components)與 Hooks
**函式元件** 是現代 React 的主流。搭配 **Hooks**,幾乎可取代 Class 元件的所有場景。
### `useState`:管理狀態
```html
<script type="text/babel">
const { useState } = React;
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
const root = document.querySelector('#root');
ReactDOM.render(<Counter />, root);
</script>
```
也可以管理**陣列或物件**狀態:
```html
<script type="text/babel">
const { useState } = React;
function Products() {
const [products, setProducts] = useState([
{ name: 'pen', price: 22 },
{ name: 'apple', price: 44 },
]);
const addProduct = () => setProducts([...products, { name: 'book', price: 55 }]);
return (
<div>
<button type="button" onClick={addProduct}>Add</button>
{products.map(({ name }) => (
<div key={name}>{name}</div>
))}
</div>
);
}
const root = document.querySelector('#root');
ReactDOM.render(<Products />, root);
</script>
```
### `useEffect`:處理副作用
```html
<script type="text/babel">
const { useState, useEffect } = React;
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
}); // 省略相依陣列:每次 render 後都會執行
// 只在掛載一次時執行:
// useEffect(() => { initSomething(); }, []);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
const root = document.querySelector('#root');
ReactDOM.render(<Counter />, root);
</script>
```
<br/>
## 簡易「路由」小練習(不靠套件)
用一個 `page` 狀態來控制要顯示的頁面內容:
```html
<script type="text/babel">
'use strict';
const { useState } = React;
const App = () => {
const [page, setPage] = useState('1');
return (
<main>
<Navbar page={page} setPage={setPage} />
<Header page={page} />
</main>
);
};
const Navbar = ({ page, setPage }) => {
const routes = [
{ name: 'link1', link: '1' },
{ name: 'link2', link: '2' },
{ name: 'link3', link: '3' },
{ name: 'link4', link: '4' },
];
return (
<nav className="test">
<ul>
{routes.map(({ name, link }) => (
<li key={name}>
<a href="#" onClick={(e) => { e.preventDefault(); setPage(link); }}>{name}</a>
</li>
))}
</ul>
</nav>
);
};
const Header = ({ page }) => <h1>{page}</h1>;
ReactDOM.render(<App />, document.getElementById('root'));
</script>
```
---
## 常見誤解 & 小提醒
* **JSX 不是 HTML**:屬性命名與 HTML 不完全相同(如 `className`、`htmlFor`)。
* **不要直接改 state**:用 Setter(如 `setCount(n + 1)`),或以函式型寫法安全地取舊值。
* **Effect 相依陣列**:缺少依賴會造成隱性 bug;ESLint 規則能幫你補齊。
* **UMD 僅適合學習/試玩**:正式專案請用現代工具鏈(Vite / Next.js)。