React Fundamentals
Components
function Hello() {
return <h1>Hello from a component!</h1>;
}
export default Hello使用元件:
import Hello from "./components/Hello";
function App() {
return (
<>
<Hello />
</>
)
}React.memo
- 用途: 用於包裹函數元件,以防止在 props 沒有更改的情況下對元件進行不必要的重新渲染。
const MyComponent = React.memo((props) => {
return <div>{props.value}</div>;
});Use Case: 當你有一個函數元件,且你確信其 props 在某些情況下不會改變時,可以用來優化渲染效能。
Props
在元件之間共享的唯讀屬性。
父元件可以傳送資料給子元件。
父元件可以傳送資料給子元件。
<Component key=value />// App.jsx
function App() {
return (
<>
<Hello name="ABC" msg="Here" emoji="💥" />
<Hello name="XYZ" msg="Nothing" emoji="🎇" />
</>
);
}
// Hello.jsx
function Hello({ name, msg, emoji }) {
return (
<div>
<h1>
Hello {name}, {msg} {emoji}
</h1>
</div>
);
}Immutability
不可變性
function Hello(props) {
// props.name = "Replace"; // Error: Uncaught TypeError: "name" is read-only
return (
<div>
<h1>
Hello {props.name}, {props.msg} {props.emoji}!
</h1>
</div>
);
}PropTypes
一種確保傳遞的值具有正確資料類型的機制。
age: PropTypes.number
Lists & Keys
export default function Fruits() {
const fruits = ["Apple", "Mango", "Orange", "Banana", "Pineapple"];
return (
<div>
<ul>
{fruits.map((fruit) => (
<li key={fruit}>{fruit}</li> // 正確: 加上唯一的 key
))}
</ul>
</div>
);
}Portal
Modal/Dialog (對話框)
- 確認對話框
- 訊息提示
- 表單輸入
懸浮元素
- Tooltip (工具提示)
- Dropdown Menu (下拉選單)
- Select 下拉選單
- DatePicker (日期選擇器)
全域通知
- Toast 通知
- 錯誤訊息
- Loading 狀態
特殊 UI
- 右鍵選單 (Context Menu)
- 虛擬鍵盤
- 圖片預覽
- 影片播放器
- 拖拽選單
特殊情境
- iframe 內的元素
- Shadow DOM 外的元素
- 第三方套件整合
使用時機:
- 需要跳出當前 DOM 階層限制
- 避免
z-index/overflow問題 - 需要共享全域 UI 元件
- 需要整合不同框架的元件
Hooks
useState
允許建立狀態變數以及一個 setter 函數,用於在虛擬 DOM 中更新其值。
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}- Native (原生 JS 對比)
<p id="count">Count: 0</p>
<button id="increment">Increment</button>
<script>
let count = 0;
document.getElementById('increment').addEventListener('click', function() {
count++;
document.getElementById('count').textContent = 'Count: ' + count;
});
</script>useEffect
作用
- 事件監聽 (Event Listeners)
- DOM 操作
- 訂閱服務 (Subscriptions, 如實時更新)
- 從 API 獲取資料
- 元件卸載時的清理工作
告訴 React 在什麼時候執行程式碼
- 當元件重新渲染時
- 當元件掛載時
- 當特定值的狀態改變時
useEffect(() => {}) // 每次渲染後執行
useEffect(() => {}, []) // 僅在掛載時執行一次
useEffect(() => {}, [value]) // 掛載時 + 當 value 改變時執行function DataFetcher() {
const [data, setData] = useState(null);
useEffect(() => {
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => setData(data));
}, []); // 空陣列表示只在元件掛載時執行一次
return <div>{data ? JSON.stringify(data) : 'Loading...'}</div>;
}useRef
當其值改變時不會引起重新渲染。
當你希望元件記憶某些資訊,但不希望該資訊觸發新的渲染時使用。
當你希望元件記憶某些資訊,但不希望該資訊觸發新的渲染時使用。
- Use Cases
- 存取 / 互動 DOM 元素
- 處理焦點 (Focus)、動畫和過渡
- 管理計時器 (Timers) 和間隔 (Intervals)
function FocusInput() {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current.focus();
};
return (
<div>
<input ref={inputRef} />
<button onClick={focusInput}>Focus Input</button>
</div>
);
}useContext
允許你在多層元件之間共享值,而不需要透過每一層手動傳遞 props。
- 使用情境
- 全域狀態 (如佈景主題、語言切換)
- 不同階層元件間的資料傳遞
Provider
// 建立 Context 並提供值Consumer
// 使用 Context 中的值useMemo
- 作用:
- 效能優化,避免不必要的重複計算
- 快取 「計算結果」
- 使用情境:
- 避免在重新渲染中執行昂貴的計算
- 優化大型資料列表的渲染
const memoizedValue = useMemo(() => {
return complexCalculation(a, b); // 快取計算結果
}, [a, b]);useCallback
- 作用:
- 效能優化,避免不必要的函數重新建立
- 快取 「函數本身」
- 使用情境:
- 當需要將函數作為
props傳遞給子元件時(搭配 React.memo) - 確保函數參照穩定,防止子元件不必要的重新渲染
- 當需要將函數作為
const memoizedCallback = useCallback(() => {
doSomething(a, b); // 快取函數參照
}, [a, b]);useReducer
- 作用: 管理複雜的狀態邏輯。
- 使用情境:
- 多步驟表單
- 圖片拖拽功能
- 複雜的狀態切換邏輯(類似 Redux)
useImperativeHandle
- 作用: 自定義由父元件可以存取的子元件實例值。
- 使用情境:
- 隱藏子元件內部實作,只暴露特定方法(如手動觸發子元件動畫)
- 複雜的可操控元件(如自定義彈窗操控)
useLayoutEffect
- 作用: 在 DOM 更新後但在瀏覽器繪製前同步執行。
- 使用情境:
- 確保在 DOM 渲染後立即進行測量或修正
- 實作複雜的同步動畫或佈局邏輯
useId
- 作用: 產生唯一的、穩定的 ID。
- 使用情境:
- 產生多個元素的唯一 Key 值
- 關聯表單標籤與輸入欄位的存取性
useTransition
- 作用: 管理非阻塞的過渡性 UI 更新。
- 使用情境:
- 在分頁切換時顯示過渡狀態
- 優化高頻率計算結果的延遲渲染
useDeferredValue
- 作用: 延遲更新某個值,以提升效能。
- 使用情境:
- 避免快速輸入時的 UI 卡頓
- 高效能的需求搜尋輸入框
useSyncExternalStore
- 作用: 訂閱外部資料源,並保持資料同步。
- 使用情境:
- 自定義全域狀態管理工具
- 連接第三方資料庫(如 Redux)
useInsertionEffect
- 作用: 在 DOM 變更前注入 CSS 樣式。
- 使用情境:
- 動態插入樣式(如 Emotion 或 styled-components)
學習順序 (Orders)
初學: 掌握最常用的 Hooks
useStateuseEffectuseContext
進階: 學習效能優化相關 Hooks
useMemouseCallbackuseRef
專家: 處理複雜狀態與邏輯的 Hooks
useReduceruseImperativeHandle
特定用途: 學習應對特殊場景的 Hooks
useLayoutEffectuseTransitionuseDeferredValueuseSyncExternalStoreuseInsertionEffectuseId
useState和useReducer用於管理元件內部的狀態。useEffect,useLayoutEffect,useInsertionEffect均用於處理副作用,差別在於執行時機。useContext用於跨元件共享資料。useRef和useId用於管理參照與唯一識別碼。useTransition和useDeferredValue用於效能優化,降低不必要的 UI 阻塞。useSyncExternalStore用於訂閱外部(非 React)的狀態。
Purity (純粹性)
React 元件應該是純函數:
function Greeting({ name }) {
return <h1>Hello, {name}!</h1>;
}- Native (原生 JS 對比)
<h1 id="greeting"></h1>
<script>
let name = 'Alice';
document.getElementById('greeting').textContent = 'Hello, ' + name + '!';
</script>Strict Mode
import React from 'react';
function MyApp() {
return (
<React.StrictMode>
<App />
</React.StrictMode>
);
}特性說明
- 開發模式下,某些生命週期會被執行兩次
- 幫助檢查副作用是否純粹
- 識別過時的 API 使用與不安全的生命週期
Fragments
function List() {
return (
<>
<li>Item 1</li>
<li>Item 2</li>
</>
);
}Context API
const ThemeContext = createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}
function ThemedComponent() {
const { theme, setTheme } = useContext(ThemeContext);
return (
<div>
<p>Current Theme: {theme}</p>
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle Theme
</button>
</div>
);
}Portals
function Modal() {
return ReactDOM.createPortal(
<div className="modal">This is a modal</div>,
document.getElementById('modal-root')
);
}Error Boundaries
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error, info) {
console.error(error, info);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}Suspense
import { Suspense, lazy } from 'react';
const LazyComponent = lazy(() => import('./LazyComponent'));
function App() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
</div>
);
}Event Handlers (事件處理器)
主要用於
每當輸入值改變時觸發函數
input, textarea, select, radio 等表單元素每當輸入值改變時觸發函數
Mouse Events
- onClick
- onDoubleClick
- onMouseDown
- onMouseUp
- onMouseMove
- onMouseEnter
- onMouseLeave
- onMouseOver
- onMouseOut
- onContextMenu
Form Events
- onChange
- onSubmit
- onReset
- onInvalid
- onSelect
Focus Events
- onFocus
- onBlur
Keyboard Events
- onKeyDown
- onKeyUp
- onKeyPress
Clipboard Events
- onCopy
- onCut
- onPaste
Touch Events
- onTouchStart
- onTouchMove
- onTouchEnd
- onTouchCancel
Drag & Drop Events
- onDrag
- onDragStart
- onDragEnd
- onDragEnter
- onDragLeave
- onDragOver
- onDrop
Media Events
- onPlay
- onPause
- onPlaying
- onEnded
- onLoadStart
- onLoadedData
- onLoadedMetadata
- onError
- onVolumeChange
- onTimeUpdate
- onSeeking
- onSeeked
Animation Events
- onAnimationStart
- onAnimationEnd
- onAnimationIteration
Transition Events
- onTransitionEnd
- onTransitionStart
Scroll Events
- onScroll
- onWheel
Window Events
- onResize
- onError
- onLoad
- onUnload
- onBeforeUnload
Visibility Events
- onVisibilityChange
Image Events
- onLoad
- onError
OnChange
function TextInput() {
const [value, setValue] = useState('');
function handleChange(e) {
setValue(e.target.value);
}
return <input value={value} onChange={handleChange} />;
}React Query
- 專門用於處理
Server Side資料- API 請求
- 資料快取
- 資料同步
- 錯誤重試
- 狀態更新
Tanstack Router
zustand
useShallow- 淺層比較訂閱的 state,減少不必要的重新渲染
- 對於物件或陣列型的 state 特別有用
- 類似於 React.memo 的比較邏輯
Middleware
immer
- 允許以可變的方式編寫不可變的更新
- 簡化複雜狀態的更新邏輯
- 自動處理不可變性 (Immutability)
subscribeWithSelector
- 可以指定監聽特定的
state變化 / 指定特定條件下觸發訂閱
devtools
- 與 Redux DevTools 整合
- 支援時光倒流調試
- 可以查看狀態變化的歷史紀錄
persist
- 提供狀態持久化功能
- 可自定義存儲引擎
- 支援加密與壓縮
App.jsx
test.jsx