跳至主要内容

78 篇文章 含有標籤「frontend」

檢視所有標籤

React Hooks useReducer 入門教學 | w3schools 學習筆記

· 閱讀時間約 3 分鐘
kdchang

前言

在 React 中,useState 是管理狀態的基礎 Hook,但當應用的狀態邏輯越來越複雜,像是涉及多個欄位更新、交叉依賴或分支邏輯,使用 useState 可能變得難以維護。這時,React 提供的 useReducer Hook 是一個更合適的選擇。

useReducer 的概念與 Redux 類似,它讓你將狀態管理邏輯集中在一個 reducer 函式中,透過 dispatch 發送動作來更新狀態,使邏輯清晰、可維護性高,非常適合用於中型到大型的應用。


重點摘要

  • useReduceruseState 類似,但更適合處理複雜邏輯或多狀態管理。

  • 語法格式為:const [state, dispatch] = useReducer(reducer, initialState)

  • reducer 是一個函式,根據傳入的 action 物件決定如何更新狀態。

  • 所有狀態更新都透過 dispatch(action) 進行。

  • 初始狀態 initialState 通常是物件或陣列。

  • 適合用在:

    • 表單資料管理
    • Todo List 或清單類資料
    • 複雜的元件邏輯

實際範例:使用 useReducer 建立 Todo 狀態管理

以下範例展示如何透過 useReducer 管理一個 Todo List 的完成狀態切換邏輯。

import { useReducer } from 'react';
import ReactDOM from 'react-dom/client';

// 初始狀態:包含兩個代辦事項
const initialTodos = [
{
id: 1,
title: 'Todo 1',
complete: false,
},
{
id: 2,
title: 'Todo 2',
complete: false,
},
];

// reducer 函式:根據 action 決定如何更新狀態
const reducer = (state, action) => {
switch (action.type) {
case 'COMPLETE':
return state.map((todo) => {
if (todo.id === action.id) {
return { ...todo, complete: !todo.complete }; // 切換完成狀態
} else {
return todo;
}
});
default:
return state; // 沒有匹配的 action 則回傳原狀態
}
};

// 元件:顯示代辦事項清單
function Todos() {
const [todos, dispatch] = useReducer(reducer, initialTodos);

// 處理 checkbox 切換
const handleComplete = (todo) => {
dispatch({ type: 'COMPLETE', id: todo.id });
};

return (
<>
{todos.map((todo) => (
<div key={todo.id}>
<label>
<input type="checkbox" checked={todo.complete} onChange={() => handleComplete(todo)} />
{todo.title}
</label>
</div>
))}
</>
);
}

// 渲染應用
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Todos />);

說明

  • initialTodos 是初始狀態,為一個陣列,包含代辦事項的 idtitlecomplete 狀態。
  • reducer 是一個純函式,根據傳入的 action 型別(此處為 "COMPLETE")來更新對應的 todo 狀態。
  • dispatch 是用來觸發 reducer 的函式,只要呼叫 dispatch({ type: "COMPLETE", id: todo.id }),就會依據 id 切換該 todo 的完成狀態。

延伸說明

上述範例僅實作了完成狀態的切換,但實務中 useReducer 更能發揮作用,因為你可以整合所有的 CRUD 操作邏輯於同一個 reducer 裡,例如:

case "ADD":
return [...state, newTodo];
case "DELETE":
return state.filter(todo => todo.id !== action.id);
case "UPDATE":
return state.map(todo => todo.id === action.id ? { ...todo, title: action.title } : todo);

這樣一來,整個應用的狀態更新都統一由 reducer 管理,讓邏輯集中且更容易除錯與擴充。


總結

useReducer 是 React 提供用來處理複雜狀態邏輯的重要工具。當你遇到以下情況時,建議考慮使用 useReducer

  • 多個狀態變數需要統一處理
  • 狀態轉換邏輯複雜且重複
  • 想將狀態管理從元件中抽離以提升可讀性

透過 useReducer,你可以實現更模組化、可維護的應用狀態邏輯,使開發效率更高、錯誤更少。

參考文件

  1. React Custom Hooks

React Hooks useRef 入門教學 | w3schools 學習筆記

· 閱讀時間約 3 分鐘
kdchang

前言

在 React 中,useRef 是一個非常實用的 Hook,它提供了一個方法來在元件重新渲染之間保留值。不同於 useState,當 useRef 的值改變時不會觸發元件重新渲染,這使它成為追蹤狀態變化、儲存 DOM 參考或避免不必要重新渲染的理想選擇。

本文將深入說明 useRef 的三種主要用途:

  1. 避免重新渲染的狀態儲存
  2. 直接操作 DOM 元素
  3. 追蹤先前的狀態值

重點摘要

  • useRef 可用來在元件間保留值而不觸發重新渲染。

  • 常用於:

    • 儲存不可變的值或變數
    • 直接存取 DOM 元素(透過 ref
    • 記錄先前的狀態(例如輸入欄位的歷史值)
  • useRef() 回傳一個包含 .current 屬性的物件。

  • 修改 ref.current 不會導致畫面重繪,因此對效能影響小。


實際範例

範例 1:避免重新渲染的計數器

使用 useRef 追蹤畫面渲染次數(如果用 useState 反而會導致無限循環):

import { useState, useEffect, useRef } from 'react';
import ReactDOM from 'react-dom/client';

function App() {
const [inputValue, setInputValue] = useState('');
const count = useRef(0);

useEffect(() => {
count.current = count.current + 1;
});

return (
<>
<input type="text" value={inputValue} onChange={(e) => setInputValue(e.target.value)} />
<h1>Render Count: {count.current}</h1>
</>
);
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

說明:

  • useRef(0) 初始化 count.current 為 0。
  • 每次渲染後,useEffect 更新 count.current
  • 即使值變了,畫面不會重新渲染,這正是 useRef 的特性。

範例 2:存取 DOM 元素

在 React 中,大部分的 DOM 操作應交由框架管理,但在特定情況下,我們需要手動聚焦、選取或操作元素,這時 useRef 就派上用場。

import { useRef } from 'react';
import ReactDOM from 'react-dom/client';

function App() {
const inputElement = useRef();

const focusInput = () => {
inputElement.current.focus();
};

return (
<>
<input type="text" ref={inputElement} />
<button onClick={focusInput}>Focus Input</button>
</>
);
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

說明:

  • 使用 ref={inputElement} 將 input 元素指向 useRef() 的回傳值。
  • 呼叫 inputElement.current.focus() 來讓輸入框聚焦。

範例 3:追蹤先前的狀態值

如果你想知道某個值在上一次渲染的狀態是什麼,可以使用 useRef 搭配 useEffect 來達成。

import { useState, useEffect, useRef } from 'react';
import ReactDOM from 'react-dom/client';

function App() {
const [inputValue, setInputValue] = useState('');
const previousInputValue = useRef('');

useEffect(() => {
previousInputValue.current = inputValue;
}, [inputValue]);

return (
<>
<input type="text" value={inputValue} onChange={(e) => setInputValue(e.target.value)} />
<h2>Current Value: {inputValue}</h2>
<h2>Previous Value: {previousInputValue.current}</h2>
</>
);
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

說明:

  • useRef 儲存上一個 inputValue
  • 每次 inputValue 改變時,useEffect 都會更新 previousInputValue.current
  • 在畫面上同時顯示目前值與上一個值。

總結

useRef 是 React 中一個簡單卻強大的工具,適用於以下情境:

  • 維持不影響渲染的狀態資料(例如計數器、定時器 ID、外部資料等)
  • 直接操作 DOM 元素(例如設定焦點、自動滾動)
  • 追蹤先前的狀態值或變數(例如表單內容變化)

理解並善用 useRef 可以幫助你更靈活地處理 React 元件中的各種非視覺狀態,讓應用程式更穩定、效能更佳。下次當你不需要觸發重新渲染時,請記得考慮使用 useRef


如果你需要我幫你將此內容轉換為 Markdown、部落格文章格式或簡報稿,也可以告訴我。

參考文件

  1. React Custom Hooks

React Hooks useContext 入門教學 | w3schools 學習筆記

· 閱讀時間約 3 分鐘
kdchang

前言

在 React 應用程式中,當需要在多個巢狀元件之間共享資料時,傳遞 props 是最基本的做法。但當元件層級變深,這樣的資料傳遞會變得繁瑣且難以維護,這種情況被稱為「props drilling」。為了解決這個問題,React 提供了 Context API,搭配 useContext Hook 可以讓你在不需要一層層傳遞 props 的情況下,輕鬆地在深層元件中讀取共享的狀態。


重點摘要

  • React Context 是一種全域狀態管理的工具。
  • 可以搭配 useState 使用,實現跨元件樹狀結構的資料共享。
  • 「prop drilling」是指一層層傳遞 props,容易造成程式碼混亂。
  • 使用 Context 包裝需要共享資料的元件樹,可避免不必要的傳遞。
  • useContext Hook 用於在任意元件中讀取指定 Context 的值。

問題背景與傳統寫法

假設我們有一個使用者名稱 user 的狀態,我們希望在最上層的元件設定這個狀態,並在最底層的第 5 個元件中使用它。傳統的做法會是逐層傳遞:

import { useState } from 'react';
import ReactDOM from 'react-dom/client';

function Component1() {
const [user, setUser] = useState('Jesse Hall');

return (
<>
<h1>{`Hello ${user}!`}</h1>
<Component2 user={user} />
</>
);
}

function Component2({ user }) {
return (
<>
<h1>Component 2</h1>
<Component3 user={user} />
</>
);
}

function Component3({ user }) {
return (
<>
<h1>Component 3</h1>
<Component4 user={user} />
</>
);
}

function Component4({ user }) {
return (
<>
<h1>Component 4</h1>
<Component5 user={user} />
</>
);
}

function Component5({ user }) {
return (
<>
<h1>Component 5</h1>
<h2>{`Hello ${user} again!`}</h2>
</>
);
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Component1 />);

雖然只有第 1 和第 5 個元件需要這個資料,但第 2 到第 4 個元件也被迫傳遞 props,造成不必要的耦合。


解決方案:使用 React Context 與 useContext Hook

步驟一:建立 Context

import { createContext } from 'react';

const UserContext = createContext();

這段程式碼建立了一個新的 Context,用來傳遞 user 的狀態。


步驟二:使用 Provider 包住需要資料的元件樹

function Component1() {
const [user, setUser] = useState('Jesse Hall');

return (
<UserContext.Provider value={user}>
<h1>{`Hello ${user}!`}</h1>
<Component2 />
</UserContext.Provider>
);
}

UserContext.Provider 負責提供資料給子元件。所有被包住的子元件都能透過 useContext 取得 user 的值。


步驟三:在需要的元件中使用 useContext 取得資料

import { useContext } from 'react';

function Component5() {
const user = useContext(UserContext);

return (
<>
<h1>Component 5</h1>
<h2>{`Hello ${user} again!`}</h2>
</>
);
}

只需一行即可取得 Context 的值,避免層層傳遞 props


完整範例程式碼

import { useState, createContext, useContext } from 'react';
import ReactDOM from 'react-dom/client';

// 建立 Context
const UserContext = createContext();

function Component1() {
const [user, setUser] = useState('Jesse Hall');

return (
<UserContext.Provider value={user}>
<h1>{`Hello ${user}!`}</h1>
<Component2 />
</UserContext.Provider>
);
}

function Component2() {
return (
<>
<h1>Component 2</h1>
<Component3 />
</>
);
}

function Component3() {
return (
<>
<h1>Component 3</h1>
<Component4 />
</>
);
}

function Component4() {
return (
<>
<h1>Component 4</h1>
<Component5 />
</>
);
}

function Component5() {
const user = useContext(UserContext);

return (
<>
<h1>Component 5</h1>
<h2>{`Hello ${user} again!`}</h2>
</>
);
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Component1 />);

總結

透過 useContext Hook 搭配 Context API,你可以有效管理全域或區域性的狀態,避免繁瑣的 props 傳遞,讓元件間的溝通更加清晰與高效。這種模式特別適用於需要在多個巢狀元件中共享資料的情境,例如主題切換、登入狀態管理、使用者資料等。

熟練掌握 React Context 與 useContext,將大幅提升你在開發大型 React 應用的能力與維護性。

參考文件

  1. React Custom Hooks

React Hooks useEffect 入門教學 | w3schools 學習筆記

· 閱讀時間約 3 分鐘
kdchang

前言

在 React 函式型元件中,useEffect 是一個強大且常用的 Hook,用來處理副作用(side effects)。副作用指的是那些不直接涉及元件渲染的操作,例如:發送 API 請求、操作 DOM、設定或清除計時器等。

傳統上,這些操作會在 componentDidMountcomponentDidUpdatecomponentWillUnmount 等生命週期函式中進行,而在函式元件中,useEffect 正是用來統一處理這些行為。


重點摘要

  • useEffect 可以執行副作用操作,例如:抓取資料、設定計時器、監聽事件。
  • 語法格式:useEffect(函式, 依賴陣列)
  • 不提供第二個參數時,useEffect 每次重新渲染都會執行。
  • 傳入空陣列作為第二個參數,則只會在元件初次渲染時執行一次。
  • 若依賴陣列中包含特定的 state 或 props,只要它們改變,副作用就會重新執行。
  • 可以在 useEffect 裡透過回傳一個函式進行資源清除(cleanup),避免記憶體洩漏。

實際範例

範例一:沒有依賴陣列,導致每次渲染都執行

import { useState, useEffect } from 'react';
import ReactDOM from 'react-dom/client';

function Timer() {
const [count, setCount] = useState(0);

useEffect(() => {
setTimeout(() => {
setCount((count) => count + 1);
}, 1000);
});

return <h1>I've rendered {count} times!</h1>;
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Timer />);

問題說明:

  • 每次渲染都會重新執行 useEffect,導致 setTimeout 一直重複,數字不斷累加,非預期行為。

範例二:使用空陣列作為依賴,只執行一次

import { useState, useEffect } from 'react';
import ReactDOM from 'react-dom/client';

function Timer() {
const [count, setCount] = useState(0);

useEffect(() => {
setTimeout(() => {
setCount((count) => count + 1);
}, 1000);
}, []); // 加上空陣列,只在初始渲染時執行一次

return <h1>I've rendered {count} times!</h1>;
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Timer />);

重點:

  • 空陣列表示沒有任何依賴,因此只會在元件掛載時執行一次副作用。

範例三:有依賴變數,根據 count 改變而重新執行

import { useState, useEffect } from 'react';
import ReactDOM from 'react-dom/client';

function Counter() {
const [count, setCount] = useState(0);
const [calculation, setCalculation] = useState(0);

useEffect(() => {
setCalculation(() => count * 2);
}, [count]); // 每次 count 改變就重新計算

return (
<>
<p>Count: {count}</p>
<button onClick={() => setCount((c) => c + 1)}>+</button>
<p>Calculation: {calculation}</p>
</>
);
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Counter />);

重點:

  • 依賴陣列中包含 count,因此只要 count 改變,useEffect 就會重新執行,計算新的值。

副作用的清除(Effect Cleanup)

某些副作用,如計時器、訂閱、事件監聽器等,當元件卸載或依賴改變時,應該清除,否則可能會導致記憶體洩漏或非預期行為。

useEffect 中可以回傳一個函式,用來執行清除動作。

範例四:清除計時器

import { useState, useEffect } from 'react';
import ReactDOM from 'react-dom/client';

function Timer() {
const [count, setCount] = useState(0);

useEffect(() => {
const timer = setTimeout(() => {
setCount((count) => count + 1);
}, 1000);

return () => clearTimeout(timer); // 清除 timeout
}, []);

return <h1>I've rendered {count} times!</h1>;
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Timer />);

說明:

  • setTimeout 被命名為 timer,在 useEffect 的清除函式中使用 clearTimeout(timer) 移除它,避免重複執行。

總結

React 的 useEffect 是處理副作用的主要工具。理解它的運作邏輯、依賴機制與清除策略,能幫助開發者更有效率地控制元件的生命周期與效能。

使用建議:

  • 若副作用只需在元件初次渲染執行,請傳入空陣列。
  • 若需要根據變數變動執行副作用,將其加入依賴陣列中。
  • 若副作用產生了外部資源(如計時器、訂閱等),務必記得清除。

掌握這些原則後,就能更靈活並安全地使用 useEffect,打造高效穩定的 React 應用。

參考文件

  1. React Custom Hooks

React Hooks useState 入門教學 | w3schools 學習筆記

· 閱讀時間約 3 分鐘
kdchang

前言

在 React 函式元件中,useState 是最常用的 Hook 之一,能讓我們在無需使用 class 的情況下新增與管理元件的「狀態」(state)。狀態是指會隨著使用者互動或應用邏輯變化而更新的資料,例如:輸入框的內容、按鈕點擊次數、切換的主題顏色等。


重點摘要

  • useState 是 React 提供的 Hook,用來在函式型元件中儲存與更新狀態。
  • 使用前需先從 react 匯入 useState
  • useState(初始值) 會回傳一個陣列,包含目前的狀態值與更新狀態的函式。
  • 更新狀態請使用 setXXX 函式,不可直接修改狀態變數
  • 可以建立多個 useState 追蹤不同變數,也可使用一個物件整合多個欄位。
  • 若要更新物件或陣列的部分內容,應使用展開運算子(spread operator)來保留其他值。

實際範例

1. 匯入 useState

import { useState } from 'react';

使用時,請確認使用的是具名匯入(named import),useStatereact 模組的一部分。


2. 初始化狀態

function FavoriteColor() {
const [color, setColor] = useState('');
}

color 是目前狀態值;setColor 是修改此狀態的函式;初始值設定為空字串。


3. 讀取狀態並渲染

function FavoriteColor() {
const [color, setColor] = useState('red');

return <h1>My favorite color is {color}!</h1>;
}

此例中,畫面上會顯示 My favorite color is red!,透過 JSX 讀取狀態。


4. 使用按鈕更新狀態

function FavoriteColor() {
const [color, setColor] = useState('red');

return (
<>
<h1>My favorite color is {color}!</h1>
<button type="button" onClick={() => setColor('blue')}>
Blue
</button>
</>
);
}

點擊按鈕時,會透過 setColorcolor 更新為 "blue",畫面也會即時更新。


5. 多個狀態變數

function Car() {
const [brand, setBrand] = useState('Ford');
const [model, setModel] = useState('Mustang');
const [year, setYear] = useState('1964');
const [color, setColor] = useState('red');

return (
<>
<h1>My {brand}</h1>
<p>
It is a {color} {model} from {year}.
</p>
</>
);
}

這種方式使用多個 useState 管理多個欄位,彼此獨立。


6. 使用物件作為單一狀態

function Car() {
const [car, setCar] = useState({
brand: 'Ford',
model: 'Mustang',
year: '1964',
color: 'red',
});

return (
<>
<h1>My {car.brand}</h1>
<p>
It is a {car.color} {car.model} from {car.year}.
</p>
</>
);
}

以物件作為狀態,可集中管理多個欄位,也使程式碼更易維護。


7. 更新物件中的單一欄位

function Car() {
const [car, setCar] = useState({
brand: 'Ford',
model: 'Mustang',
year: '1964',
color: 'red',
});

const updateColor = () => {
setCar((prevState) => {
return { ...prevState, color: 'blue' };
});
};

return (
<>
<h1>My {car.brand}</h1>
<p>
It is a {car.color} {car.model} from {car.year}.
</p>
<button type="button" onClick={updateColor}>
Blue
</button>
</>
);
}

這裡使用展開運算子(...prevState)來保留其他屬性,僅更新 color。若直接使用 setCar({ color: "blue" }),會造成其他屬性遺失。


總結

React 的 useState 是建立互動式 UI 的基礎,能讓我們在函式型元件中管理狀態。透過這個 Hook,我們可以:

  • 初始化與讀取狀態值
  • 透過更新函式改變狀態並重新渲染畫面
  • 使用多個 useState 管理多個資料
  • 或整合為一個物件並使用展開運算子更新部分欄位

熟練掌握 useState 是學會 React 開發不可或缺的第一步。建議初學者透過實作各種小範例來加深理解與記憶。

參考文件

  1. React Custom Hooks

React Hooks 入門教學 | w3schools 學習筆記

· 閱讀時間約 2 分鐘
kdchang

前言

Hooks 是在 React 16.8 版本中加入的新功能。Hooks 讓你可以在「函式元件(function components)」中使用狀態(state)以及其他 React 功能。因此,自從有了 Hooks 之後,類別元件(class components)通常就不再是必要的了

儘管 Hooks 幾乎取代了類別元件的使用方式,但 React 團隊目前並沒有打算移除 class 元件的支援。


什麼是 Hook?

Hooks 讓我們能夠「掛勾(hook into)」React 的核心功能,例如 狀態管理(state)生命週期方法(lifecycle methods)


範例:

import React, { useState } from 'react';
import ReactDOM from 'react-dom/client';

function FavoriteColor() {
const [color, setColor] = useState('red');

return (
<>
<h1>我最喜歡的顏色是 {color}</h1>
<button type="button" onClick={() => setColor('blue')}>
藍色
</button>
<button type="button" onClick={() => setColor('red')}>
紅色
</button>
<button type="button" onClick={() => setColor('pink')}>
粉紅色
</button>
<button type="button" onClick={() => setColor('green')}>
綠色
</button>
</>
);
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<FavoriteColor />);

在這個範例中,我們透過 useState 這個 Hook 來追蹤應用程式的狀態。

狀態(State):泛指那些會隨著使用者互動或應用邏輯而改變的資料或屬性。

此外,使用 Hook 前必須先從 react 模組中引入對應的函式,例如這裡的 useState


使用 Hook 的三大規則

  1. 只能在 React 的函式元件中呼叫 Hooks
  2. 只能在元件的最上層(Top-level)呼叫,不能寫在 iffor函式中等區塊內
  3. 不能有條件式地呼叫 Hook(例如不能寫在 if 判斷中)

注意:Hooks 無法在 class 類別元件中使用!


自訂 Hook(Custom Hooks)

如果你有一些包含狀態的邏輯(stateful logic),需要在多個元件之間重複使用,這時可以考慮封裝成自訂 Hook,以提升可讀性與重用性。

參考文件

  1. React Custom Hooks

在 React 中使用 Sass 進行樣式設計教學 | w3schools 學習筆記

· 閱讀時間約 3 分鐘
kdchang

前言

在現代前端開發中,維護與管理 CSS 樣式是一項重要任務,尤其當應用程式日益龐大、元件複雜時,單純使用原生 CSS 經常會遇到樣式難以重用、命名衝突等問題。這時候,Sass(Syntactically Awesome Stylesheets)這類 CSS 預處理器便顯得格外實用。Sass 提供變數、巢狀語法、Mixin 等強大功能,有助於讓樣式更具模組化與可維護性。

本篇教學將說明如何在 React 專案中使用 Sass,從安裝、建立樣式檔案、到實際在元件中引用,手把手帶你完成設定。


重點摘要

  • Sass 是什麼:一種 CSS 預處理器,可在瀏覽器載入前編譯成標準 CSS。
  • 安裝方法:可透過 npm i sass 安裝 Sass 至 React 專案中。
  • 副檔名:Sass 檔案使用 .scss 副檔名。
  • 支援變數與函數:可使用 $變數@mixin 等進階語法撰寫樣式。
  • 與 React 整合方式:與 CSS 類似,透過 import './樣式.scss' 導入樣式。

Sass 是什麼?

Sass(Syntactically Awesome Stylesheets)是一種 CSS 的擴充語法,稱為 CSS 預處理器(preprocessor)。Sass 檔案會在伺服器端進行編譯,轉換為標準的 CSS,然後再由瀏覽器載入。

與傳統 CSS 相比,Sass 提供多種程式化的功能,包括:

  • 變數(Variables)
  • 巢狀語法(Nesting)
  • 混合(Mixins)
  • 繼承(Inheritance)

這些功能可以大幅簡化樣式維護與邏輯。


如何在 React 中使用 Sass?

若你使用 vite 建立專案,只需要簡單幾步即可在 React 中整合 Sass。

安裝 Sass

打開終端機,並在 React 專案目錄中執行以下指令:

npm i sass

安裝完成後,即可開始在專案中撰寫與導入 .scss 檔案。


建立 Sass 檔案

建立 .scss 檔案的方式與 CSS 相同,唯一差別是副檔名由 .css 改為 .scss

假設建立一個名為 my-sass.scss 的檔案,其內容如下:

// 定義一個變數
$myColor: red;

// 使用變數設定 h1 的文字顏色
h1 {
color: $myColor;
}

這段 Sass 程式碼定義了一個 $myColor 的變數,並將其應用於 h1 標題文字的顏色。


在 React 元件中使用 Sass

要在 React 元件中使用 Sass,只需像導入 CSS 檔案一樣導入 .scss 檔案即可。

以下是一個完整範例:

index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './my-sass.scss'; // 導入 Sass 樣式檔

// 建立一個簡單的元件
const Header = () => {
return (
<>
<h1>Hello Style!</h1>
<p>Add a little style!.</p>
</>
);
};

// 掛載元件到畫面上
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Header />);

只要這樣導入 Sass 檔案,該樣式就會自動被應用到對應的元素上,與原生 CSS 的使用方式幾乎一致。


延伸學習建議

若你對 Sass 有更進一步的興趣,建議深入學習以下主題:

  • Sass Mixin 的建立與使用
  • 巢狀規則與 BEM 命名法結合技巧
  • Sass 的分割與模組化導入(@import / @use)
  • 與 CSS Modules、Styled-components 等工具的比較與選擇

總結

Sass 為 CSS 帶來更多彈性與可維護性,是開發大型 React 應用時的重要利器。配合 vite 的簡單整合方式,即使是初學者也能快速上手。只需幾步就能建立出具備變數與結構邏輯的樣式系統,為專案帶來更清晰、可維護的樣式架構。

無論是個人 Side Project 或是商業應用,學會 Sass 的使用,將大大提升你在前端開發上的效率與品質。

參考文件

  1. React Custom Hooks
  2. React router 官方網站

React 中使用 CSS 的三種方式教學 | w3schools 學習筆記

· 閱讀時間約 4 分鐘
kdchang

前言

在開發 React 應用程式時,樣式的管理與設計是一項不可忽視的重要部分。雖然 React 是一個 JavaScript 函式庫,主要用於建構使用者介面,但它本身並不限制開發者如何為元件加上樣式。React 提供多種整合 CSS 的方法,這篇文章將會深入介紹三種最常見的樣式處理方式:行內樣式(Inline Styling)CSS 樣式表(Stylesheet)以及CSS 模組(CSS Modules),並說明每種方式的使用情境與範例。


重點摘要

  • React 支援使用 JavaScript 對元件進行行內樣式設計。
  • 傳統 CSS 樣式表可以與 React 搭配使用,只需匯入對應檔案。
  • 使用 CSS Modules 可避免樣式名稱衝突,適用於大型專案。

一、行內樣式(Inline Styling)

React 提供以 JavaScript 物件的方式設定行內樣式,這種方式最直接也最簡單,適合少量樣式或動態樣式情境。

實作範例:

const Header = () => {
return (
<>
<h1 style={{ color: 'red' }}>Hello Style!</h1>
<p>Add a little style!</p>
</>
);
};

**注意:**在 JSX 中,JavaScript 表達式必須寫在大括號 {} 內,而行內樣式本身是一個物件,因此需使用雙層大括號 {{}}


屬性名稱需使用 camelCase 命名方式

在 JavaScript 中無法使用帶有連字符(例如 background-color)的 CSS 屬性名稱,因此需轉換為 camelCase,例如:backgroundColor

實作範例:

const Header = () => {
return (
<>
<h1 style={{ backgroundColor: 'lightblue' }}>Hello Style!</h1>
<p>Add a little style!</p>
</>
);
};

使用樣式物件來管理樣式

若樣式較多,可先定義一個樣式物件,再以變數形式傳入 style 屬性中。

const Header = () => {
const myStyle = {
color: 'white',
backgroundColor: 'DodgerBlue',
padding: '10px',
fontFamily: 'Sans-Serif',
};

return (
<>
<h1 style={myStyle}>Hello Style!</h1>
<p>Add a little style!</p>
</>
);
};

二、CSS 樣式表(Stylesheet)

若你偏好將樣式與程式邏輯分離,傳統的 CSS 樣式表仍然是實用的選擇。只需建立 .css 檔案並在元件或主程式中匯入即可。

步驟一:建立 App.css 檔案

/* App.css */
body {
background-color: #282c34;
color: white;
padding: 40px;
font-family: Sans-Serif;
text-align: center;
}

**提示:**檔案名稱可以自由命名,但副檔名需為 .css

步驟二:匯入樣式表

// index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './App.css';

const Header = () => {
return (
<>
<h1>Hello Style!</h1>
<p>Add a little style!</p>
</>
);
};

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Header />);

這種方式與傳統 HTML 開發流程相似,適合簡單專案或共用樣式的情境。


三、CSS Modules

CSS Modules 是一種模組化的 CSS 寫法,每個 CSS 檔案的樣式只作用於匯入該檔案的元件,能有效避免樣式衝突,特別適合大型應用程式或多人協作的開發環境。

步驟一:建立模組化樣式檔案

建立一個名稱為 my-style.module.css 的 CSS 檔案:

/* my-style.module.css */
.bigblue {
color: DodgerBlue;
padding: 40px;
font-family: Sans-Serif;
text-align: center;
}

**注意:**檔案名稱需符合 *.module.css 格式,才能啟用模組功能。

步驟二:在元件中匯入模組

// Car.js
import styles from './my-style.module.css';

const Car = () => {
return <h1 className={styles.bigblue}>Hello Car!</h1>;
};

export default Car;

步驟三:匯入元件至主程式

// index.js
import ReactDOM from 'react-dom/client';
import Car from './Car.js';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Car />);

透過這種方式,.bigblue 樣式只會應用於 Car 元件,其他元件不會受到影響。


總結

React 提供靈活的方式來處理樣式,你可以根據專案規模與開發需求選擇最適合的做法:

  • 小型元件或需動態切換樣式 → 行內樣式
  • 簡單專案或共用樣式 → 外部樣式表
  • 中大型專案或多人開發 → CSS Modules

透過良好的樣式管理方式,不僅可以提升 UI 的一致性與可維護性,也讓你能更專注於元件的邏輯設計。

如果你正開始學習 React,建議從行內樣式入手,漸進式學習 CSS Modules,將有助於建立健全的開發習慣與架構。

參考文件

  1. React Custom Hooks
  2. React router 官方網站

提升效能的利器:React.memo 使用入門教學 | w3schools 學習筆記

· 閱讀時間約 4 分鐘
kdchang

前言

在 React 中,當元件的父元件重新渲染時,預設情況下其所有子元件也會一併重新渲染,即便傳入的 props 完全沒變動。這種「不必要的重新渲染」若發生在大型應用中,會導致效能下降,尤其是當某些元件非常複雜、包含大量計算或 DOM 操作時。

為了避免這種情況,React 提供了一個高階元件函式:React.memo。這個函式能讓你記憶元件的輸出結果,當傳入的 props 沒有變更時,React 就會跳過該元件的重新渲染,達到提升效能的效果。


重點摘要

  • React.memo 可讓函式型元件根據 props 的淺層比較決定是否重新渲染
  • 適合用在接收不變 props 的純顯示元件
  • props 為函式、物件、陣列時,注意傳入參考要穩定,否則仍會觸發重新渲染
  • 效能提升明顯的情況:元件內容複雜、資料量大或頻繁更新的應用場景
  • 搭配 useCallbackuseMemo 可進一步優化

問題說明

以下是一個簡單的 React 應用,包含一個計數器與待辦事項列表元件(Todos):

index.js

import { useState } from 'react';
import ReactDOM from 'react-dom/client';
import Todos from './Todos';

const App = () => {
const [count, setCount] = useState(0);
const [todos, setTodos] = useState(['todo 1', 'todo 2']);

const increment = () => {
setCount((c) => c + 1);
};

return (
<>
<Todos todos={todos} />
<hr />
<div>
Count: {count}
<button onClick={increment}>+</button>
</div>
</>
);
};

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

Todos.js

const Todos = ({ todos }) => {
console.log('child render');
return (
<>
<h2>My Todos</h2>
{todos.map((todo, index) => {
return <p key={index}>{todo}</p>;
})}
</>
);
};

export default Todos;

在這個範例中,每當你點擊「+」按鈕讓 count 增加時,整個 App 元件重新渲染,而 Todos 元件也會一併被重新渲染,即使 todos 陣列完全沒有變更。你可以從 console log 中觀察到 child render 不斷出現。

若這個 Todos 元件變得很複雜,這樣的重新渲染就會造成效能浪費。


解法:使用 React.memo

要解決這個問題,我們可以使用 memoTodos 元件包裝起來,使其只有在 props.todos 變動時才會重新渲染。

修改 Todos.js

import { memo } from 'react';

const Todos = ({ todos }) => {
console.log('child render');
return (
<>
<h2>My Todos</h2>
{todos.map((todo, index) => {
return <p key={index}>{todo}</p>;
})}
</>
);
};

export default memo(Todos);

這裡我們引入 memo 並用它包裝元件。這樣一來,只要 todos 的內容沒有改變,Todos 元件就不會重新執行 render。


行為比較與效能提升說明

行為未使用 memo使用 memo
count 變更時重新渲染 Todos不重新渲染 Todos
todos 改變時重新渲染 Todos重新渲染 Todos
效能影響易造成浪費避免不必要更新

延伸說明

  • memo 比較的是 props 的淺層相等性。若傳入的是物件、函式等「參考型資料」,每次 render 都會被視為不同,仍然會觸發重新渲染。
  • 若要避免這種情況,可搭配 useMemouseCallback 讓 props 穩定。
  • memo 並非萬能,只有在元件內容複雜、render 成本高時,才明顯帶來效能優化。過度使用反而會增加記憶體與比較成本。

小結

React.memo 是 React 應用中常見的效能優化技巧之一。透過對 props 進行淺層比較,可以有效避免不必要的子元件重新渲染。當你的應用中包含大量重複渲染的靜態元件或資料時,適時使用 memo 可以大幅提升效能表現。

建議在開發時養成習慣:只有當元件的 props 確實可能不會變動,且 render 成本高時再使用 memo,讓效能優化達到真正效果。

參考文件

  1. React Custom Hooks
  2. React router 官方網站

使用 React Router 實現多頁面導覽功能教學(React Router v6)入門教學 | w3schools 學習筆記

· 閱讀時間約 4 分鐘
kdchang

前言

React 是一個強大的 JavaScript 函式庫,專門用於建立使用者介面。不過,React 本身並未內建「頁面路由」功能。如果你想要為你的 React 專案加入多個頁面,例如首頁、部落格、聯絡我們頁面等等,就必須引入額外的工具。而在眾多路由解決方案中,React Router 是最受歡迎且廣泛使用的選擇。

本文將帶你從零開始,教你如何在使用 Create React App 建立的專案中導入 React Router,並建立一個基本的多頁面架構。


重點摘要

  • React 本身不包含頁面路由功能
  • React Router 是 React 中最常用的路由套件
  • React Router v6 是目前最新的主要版本
  • 需安裝 react-router-dom 套件來使用瀏覽器路由功能
  • 使用 <BrowserRouter>, <Routes>, <Route> 建立路由結構
  • 使用 <Outlet> 顯示巢狀路由對應的內容
  • 使用 <Link> 而非 <a> 進行頁面內部連結

安裝 React Router

在你的 React 專案根目錄下,打開終端機,執行以下指令安裝 React Router:

npm i -D react-router-dom

如果你是從 React Router v5 升級,建議加入 @latest 旗標:

npm i -D react-router-dom@latest

建立頁面資料夾與基本結構

為了建立多頁面應用,我們需要在 src 資料夾中新增一個 pages 資料夾,並在其中建立五個頁面元件:

src/pages/
├── Layout.js
├── Home.js
├── Blogs.js
├── Contact.js
└── NoPage.js

每個檔案都將包含一個簡單的 React 函式元件。


設定主路由(index.js)

src/index.js 中引入路由模組與頁面元件,並建立應用程式主結構:

import ReactDOM from 'react-dom/client';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Layout from './pages/Layout';
import Home from './pages/Home';
import Blogs from './pages/Blogs';
import Contact from './pages/Contact';
import NoPage from './pages/NoPage';

export default function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="blogs" element={<Blogs />} />
<Route path="contact" element={<Contact />} />
<Route path="*" element={<NoPage />} />
</Route>
</Routes>
</BrowserRouter>
);
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

說明範例運作原理

  • <BrowserRouter>:外層包住整個路由結構,提供瀏覽器路由功能。
  • <Routes><Route>:定義所有路由規則與對應的元件。
  • 巢狀 <Route>Layout 元件作為共同外框,其下包含巢狀頁面路由。
  • <Route index>:定義 / 路徑的預設頁面為 Home
  • <Route path="*">:匹配所有未定義的網址,用於顯示 404 頁面。

建立頁面元件

Layout.js(共享頁面結構)

import { Outlet, Link } from 'react-router-dom';

const Layout = () => {
return (
<>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/blogs">Blogs</Link>
</li>
<li>
<Link to="/contact">Contact</Link>
</li>
</ul>
</nav>

<Outlet />
</>
);
};

export default Layout;
  • 使用 <Link> 元素建立頁面間的連結。
  • <Outlet> 負責渲染目前選中的頁面內容。

Home.js

const Home = () => {
return <h1>Home</h1>;
};

export default Home;

Blogs.js

const Blogs = () => {
return <h1>Blog Articles</h1>;
};

export default Blogs;

Contact.js

const Contact = () => {
return <h1>Contact Me</h1>;
};

export default Contact;

NoPage.js(404 頁面)

const NoPage = () => {
return <h1>404</h1>;
};

export default NoPage;

總結

透過 React Router,我們可以很輕鬆地為 React 應用程式建立一個多頁面的瀏覽體驗。本篇教學展示了如何安裝 React Router、建立路由結構、撰寫頁面元件,並結合 <Outlet><Link> 實現共用頁面框架與路由切換。這只是入門,React Router v6 還支援更多進階功能,例如動態路由、路由守衛、路由參數等,適合進一步探索使用。

參考文件

  1. React Custom Hooks
  2. React router 官方網站