關鍵點
- useContext:React 提供的 Hook,用于在組件樹中共享全局狀態,簡化跨組件數據傳遞。
- 應用場景:主題切換、用戶認證、語言設置和全局配置管理。
- 實現方式:結合
createContext
和useContext
,實現靈活的狀態共享。 - 優化策略:包括性能優化、狀態分割、可訪問性和手機端適配。
- 常見問題:過度使用導致耦合、不必要的重渲染和復雜狀態管理。
- 實踐場景:通過一個多語言文檔管理應用,展示
useContext
的實現與優化。
引言
在 React 應用中,組件間的數據共享是常見需求。傳統的 Props 傳遞方式在深層組件樹中可能導致“Props 鉆透”(Prop Drilling),增加代碼復雜性和維護成本。useContext
作為 React 的內置 Hook,結合 createContext
和 Provider
,提供了一種優雅的解決方案,用于在組件樹中共享全局狀態,無需逐層傳遞 Props。useContext
適用于主題切換、用戶認證、語言設置等場景,極大地簡化了狀態管理。
然而,useContext
的使用并非沒有挑戰。過度使用可能導致組件耦合,不合理的狀態設計可能引發性能問題。本文通過構建一個基于 React 的多語言文檔管理應用,深入探討 useContext
的工作原理、實現方式和優化實踐。我們將實現主題切換、語言設置和用戶認證功能,結合性能優化、可訪問性和手機端適配,提供詳細的代碼示例和場景分析,幫助開發者掌握 useContext
的核心技術和最佳實踐。
在現代 React 應用中,組件間的數據共享是構建動態用戶界面的核心需求。傳統的 Props 傳遞方式雖然簡單,但在深層嵌套的組件樹中,逐層傳遞 Props 會導致代碼冗余和維護困難,這種現象被稱為“Props 鉆透”。React 的 useContext
Hook 結合 createContext
和 Provider
,提供了一種高效的全局狀態共享機制,允許開發者在組件樹中的任何位置訪問共享數據,無需顯式傳遞 Props。
useContext
適用于多種場景,如主題切換、用戶認證、語言設置和全局配置管理。它的簡潔性和靈活性使其成為中小型項目的理想選擇。然而,useContext
的使用需要注意性能優化、狀態設計和組件耦合問題。本文通過一個基于 React 的多語言文檔管理應用,全面探討 useContext
的工作原理、實現方式和優化策略。我們將實現多語言切換、主題管理和用戶認證功能,并提供性能優化、可訪問性和手機端適配的實踐方案。
通過本項目,您將學習到:
- useContext 基礎:創建和使用 Context 實現數據共享。
- 高級功能:結合
useReducer
和useContext
實現復雜狀態管理。 - 性能優化:使用狀態分割和
React.memo
減少重渲染。 - 可訪問性:為動態內容添加 ARIA 屬性,支持屏幕閱讀器。
- 手機端適配:優化響應式布局和觸控交互。
- 部署:將應用部署到 Vercel,支持高可用性和 CDN 加速。
本文面向有經驗的開發者,假設您熟悉 HTML、CSS、JavaScript、React 和 TypeScript 基礎知識。內容詳實且實用,適合深入學習 useContext
的應用和優化。
需求分析
在動手編碼之前,我們需要明確多語言文檔管理應用的功能需求。一個清晰的需求清單能指導開發過程并幫助我們優化 useContext
的使用。以下是項目的核心需求:
- 多語言支持
- 支持切換語言(如中文、英文、西班牙文)。
- 動態更新 UI 文本(如按鈕、標題)。
- 持久化語言設置(如存儲到 localStorage)。
- 主題管理
- 支持切換亮色和暗色主題。
- 動態應用主題樣式(如背景色、文本色)。
- 用戶認證
- 管理用戶登錄狀態(如用戶名、角色)。
- 支持登錄和注銷功能。
- 文檔管理
- 顯示文檔列表,支持過濾和搜索。
- 提供文檔預覽功能。
- React 集成
- 使用
useContext
共享語言、主題和用戶狀態。 - 結合
useReducer
管理復雜狀態邏輯。
- 使用
- 性能優化
- 避免不必要的重渲染。
- 分割 Context 減少組件依賴。
- 可訪問性(a11y)
- 為動態內容添加 ARIA 屬性。
- 支持鍵盤導航和屏幕閱讀器。
- 手機端適配
- 響應式布局,適配不同屏幕尺寸。
- 優化觸控交互(如點擊、滑動)。
- 部署
- 集成到 Vite 項目,部署到 Vercel。
- 支持 CDN 加速靜態資源加載。
需求背后的意義
這些需求覆蓋了 useContext
的核心應用場景,同時為學習狀態管理和優化提供了實踐機會:
- 多語言支持:展示
useContext
在全局配置中的應用。 - 主題管理:實現動態 UI 更新,優化用戶體驗。
- 用戶認證:管理全局用戶狀態,模擬真實業務場景。
- 性能優化:解決 Context 導致的重渲染問題。
- 可訪問性:滿足無障礙標準,擴大用戶覆蓋。
- 手機端適配:適配移動設備,提升用戶體驗。
技術棧選擇
在實現多語言文檔管理應用之前,我們需要選擇合適的技術棧。以下是本項目使用的工具和技術,以及選擇它們的理由:
- React 18
核心前端框架,支持組件化開發和并發渲染,適合動態應用。 - TypeScript
提供類型安全,增強代碼可維護性和 IDE 補全,適合復雜項目。 - Vite
構建工具,提供快速的開發服務器和高效的打包能力。 - React Query
數據獲取和狀態管理庫,簡化異步數據處理。 - Tailwind CSS
提供靈活的樣式解決方案,支持響應式設計。 - Vercel
用于部署應用,提供高可用性和全球 CDN 支持。
技術棧優勢
- React 18:支持并發渲染,優化復雜應用性能。
- TypeScript:提升代碼質量,減少運行時錯誤。
- Vite:啟動速度快,熱更新體驗優越。
- React Query:簡化異步數據管理,優化文檔加載。
- Tailwind CSS:簡化樣式開發,支持響應式設計。
- Vercel:與 React 生態深度整合,部署簡單。
這些工具組合不僅易于上手,還能幫助開發者掌握 useContext
的最佳實踐。
項目實現
現在進入核心部分——代碼實現。我們將從項目搭建開始,逐步實現多語言切換、主題管理、用戶認證和文檔管理,結合性能優化、可訪問性和部署。
1. 項目搭建
使用 Vite 創建一個 React + TypeScript 項目:
npm create vite@latest doc-manager -- --template react-ts
cd doc-manager
npm install
npm run dev
安裝必要的依賴:
npm install @tanstack/react-query tailwindcss postcss autoprefixer
初始化 Tailwind CSS:
npx tailwindcss init -p
編輯 tailwind.config.js
:
/** @type {import('tailwindcss').Config} */
export default {content: ["./index.html","./src/**/*.{js,ts,jsx,tsx}",],theme: {extend: {},},plugins: [],
}
在 src/index.css
中引入 Tailwind:
@tailwind base;
@tailwind components;
@tailwind utilities;
2. 組件拆分
我們將應用拆分為以下組件:
- App:根組件,負責整體布局和 Context 提供者。
- DocumentList:顯示文檔列表,支持過濾和搜索。
- DocumentPreview:預覽選中的文檔。
- ThemeToggle:切換亮色和暗色主題。
- LanguageSelector:選擇語言,動態更新 UI。
- AuthPanel:管理用戶登錄和注銷。
- AccessibilityPanel:管理可訪問性設置。
文件結構
src/
├── components/
│ ├── DocumentList.tsx
│ ├── DocumentPreview.tsx
│ ├── ThemeToggle.tsx
│ ├── LanguageSelector.tsx
│ ├── AuthPanel.tsx
│ └── AccessibilityPanel.tsx
├── contexts/
│ ├── ThemeContext.ts
│ ├── LanguageContext.ts
│ ├── AuthContext.ts
├── hooks/
│ └── useDocuments.ts
├── types/
│ └── index.ts
├── App.tsx
├── main.tsx
└── index.css
3. Context 實現
3.1 主題管理
src/contexts/ThemeContext.ts
:
import { createContext, useContext, useState, useEffect } from 'react';interface ThemeContextType {theme: 'light' | 'dark';toggleTheme: () => void;
}const ThemeContext = createContext<ThemeContextType | undefined>(undefined);export function ThemeProvider({ children }: { children: React.ReactNode }) {const [theme, setTheme] = useState<'light' | 'dark'>('light');useEffect(() => {const savedTheme = localStorage.getItem('theme') as 'light' | 'dark' | null;if (savedTheme) {setTheme(savedTheme);document.documentElement.classList.toggle('dark', savedTheme === 'dark');}}, []);const toggleTheme = () => {setTheme(prev => {const newTheme = prev === 'light' ? 'dark' : 'light';localStorage.setItem('theme', newTheme);document.documentElement.classList.toggle('dark', newTheme === 'dark');return newTheme;});};return (<ThemeContext.Provider value={{ theme, toggleTheme }}>{children}</ThemeContext.Provider>);
}export function useTheme() {const context = useContext(ThemeContext);if (!context) {throw new Error('useTheme 必須在 ThemeProvider 內使用');}return context;
}
src/components/ThemeToggle.tsx
:
import { useTheme } from '../contexts/ThemeContext';function ThemeToggle() {const { theme, toggleTheme } = useTheme();return (<div className="p-4 bg-white dark:bg-gray-800 rounded-lg shadow"><h2 className="text-xl font-bold mb-4 text-gray-900 dark:text-white">主題切換</h2><buttononClick={toggleTheme}className="px-4 py-2 bg-blue-500 text-white rounded-lg"aria-label={`切換到${theme === 'light' ? '暗色模式' : '亮色模式'}`}>{theme === 'light' ? '暗色模式' : '亮色模式'}</button></div>);
}export default ThemeToggle;
實現過程:
- 創建
ThemeContext
共享主題狀態和切換函數。 - 使用
useEffect
持久化主題設置。 - 動態更新
document.documentElement
的類名。
避坑:
- 確保
useContext
在Provider
內部使用。 - 使用 Tailwind 的
dark:
類實現主題樣式。
3.2 語言管理
src/contexts/LanguageContext.ts
:
import { createContext, useContext, useState, useEffect } from 'react';interface LanguageContextType {language: 'zh' | 'en' | 'es';setLanguage: (lang: 'zh' | 'en' | 'es') => void;t: (key: string) => string;
}const translations = {zh: {title: '文檔管理器',search: '搜索文檔',login: '登錄',logout: '注銷',},en: {title: 'Document Manager',search: 'Search Documents',login: 'Login',logout: 'Logout',},es: {title: 'Gestor de Documentos',search: 'Buscar Documentos',login: 'Iniciar Sesión',logout: 'Cerrar Sesión',},
};const LanguageContext = createContext<LanguageContextType | undefined>(undefined);export function LanguageProvider({ children }: { children: React.ReactNode }) {const [language, setLanguage] = useState<'zh' | 'en' | 'es'>('zh');useEffect(() => {const savedLang = localStorage.getItem('language') as 'zh' | 'en' | 'es' | null;if (savedLang) {setLanguage(savedLang);}}, []);const t = (key: string) => translations[language][key] || key;return (<LanguageContext.Provider value={{ language, setLanguage, t }}>{children}</LanguageContext.Provider>);
}export function useLanguage() {const context = useContext(LanguageContext);if (!context) {throw new Error('useLanguage 必須在 LanguageProvider 內使用');}return context;
}
src/components/LanguageSelector.tsx
:
import { useLanguage } from '../contexts/LanguageContext';function LanguageSelector() {const { language, setLanguage, t } = useLanguage();return (<div className="p-4 bg-white dark:bg-gray-800 rounded-lg shadow"><h2 className="text-xl font-bold mb-4 text-gray-900 dark:text-white">{t('title')}</h2><selectvalue={language}onChange={e => setLanguage(e.target.value as 'zh' | 'en' | 'es')}className="p-2 border rounded-lg"aria-label="選擇語言"><option value="zh">中文</option><option value="en">English</option><option value="es">Espa?ol</option></select></div>);
}export default LanguageSelector;
避坑:
- 提供默認翻譯,防止未定義鍵。
- 持久化語言設置到 localStorage。
3.3 用戶認證
src/contexts/AuthContext.ts
:
import { createContext, useContext, useReducer } from 'react';interface AuthState {isAuthenticated: boolean;user: { username: string; role: 'admin' | 'user' } | null;
}type AuthAction =| { type: 'LOGIN'; payload: { username: string; role: 'admin' | 'user' } }| { type: 'LOGOUT' };interface AuthContextType {state: AuthState;login: (username: string, role: 'admin' | 'user') => void;logout: () => void;
}const AuthContext = createContext<AuthContextType | undefined>(undefined);function authReducer(state: AuthState, action: AuthAction): AuthState {switch (action.type) {case 'LOGIN':return { isAuthenticated: true, user: action.payload };case 'LOGOUT':return { isAuthenticated: false, user: null };default:return state;}
}export function AuthProvider({ children }: { children: React.ReactNode }) {const [state, dispatch] = useReducer(authReducer, { isAuthenticated: false, user: null });const login = (username: string, role: 'admin' | 'user') => {dispatch({ type: 'LOGIN', payload: { username, role } });};const logout = () => {dispatch({ type: 'LOGOUT' });};return (<AuthContext.Provider value={{ state, login, logout }}>{children}</AuthContext.Provider>);
}export function useAuth() {const context = useContext(AuthContext);if (!context) {throw new Error('useAuth 必須在 AuthProvider 內使用');}return context;
}
src/components/AuthPanel.tsx
:
import { useState } from 'react';
import { useAuth } from '../contexts/AuthContext';
import { useLanguage } from '../contexts/LanguageContext';function AuthPanel() {const { state, login, logout } = useAuth();const { t } = useLanguage();const [username, setUsername] = useState('');const handleLogin = () => {if (username) {login(username, 'user');}};return (<div className="p-4 bg-white dark:bg-gray-800 rounded-lg shadow"><h2 className="text-xl font-bold mb-4 text-gray-900 dark:text-white">用戶認證</h2>{state.isAuthenticated ? (<div><p aria-live="polite">歡迎, {state.user?.username}</p><buttononClick={logout}className="px-4 py-2 bg-red-500 text-white rounded-lg"aria-label={t('logout')}>{t('logout')}</button></div>) : (<div className="flex flex-col space-y-4"><inputtype="text"value={username}onChange={e => setUsername(e.target.value)}className="p-2 border rounded-lg"placeholder="用戶名"aria-label="輸入用戶名"/><buttononClick={handleLogin}className="px-4 py-2 bg-blue-500 text-white rounded-lg"aria-label={t('login')}>{t('login')}</button></div>)}</div>);
}export default AuthPanel;
避坑:
- 使用
useReducer
管理復雜狀態邏輯。 - 確保狀態更新觸發正確的重渲染。
4. 文檔管理
src/hooks/useDocuments.ts
:
import { useQuery } from '@tanstack/react-query';
import type { Document } from '../types';export function useDocuments() {return useQuery<Document[]>({queryKey: ['documents'],queryFn: async () => {// 模擬 API 調用await new Promise(resolve => setTimeout(resolve, 1000));return [{ id: 1, title: '報告 A', content: '這是報告 A 的內容' },{ id: 2, title: '報告 B', content: '這是報告 B 的內容' },];},});
}
src/types/index.ts
:
export interface Document {id: number;title: string;content: string;
}
src/components/DocumentList.tsx
:
import { memo } from 'react';
import { useDocuments } from '../hooks/useDocuments';
import { useLanguage } from '../contexts/LanguageContext';function DocumentList({ onSelect }: { onSelect: (doc: Document) => void }) {const { data: documents, isLoading } = useDocuments();const { t } = useLanguage();if (isLoading) {return <div className="p-4">加載中...</div>;}return (<div className="p-4 bg-white dark:bg-gray-800 rounded-lg shadow"><h2 className="text-xl font-bold mb-4 text-gray-900 dark:text-white">{t('title')}</h2><inputtype="text"className="p-2 border rounded-lg mb-4 w-full"placeholder={t('search')}aria-label={t('search')}/><ul>{documents?.map(doc => (<likey={doc.id}className="p-2 cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700"onClick={() => onSelect(doc)}role="button"aria-label={`查看文檔 ${doc.title}`}>{doc.title}</li>))}</ul></div>);
}export default memo(DocumentList);
src/components/DocumentPreview.tsx
:
import { memo } from 'react';
import type { Document } from '../types';function DocumentPreview({ document }: { document: Document | null }) {if (!document) {return <div className="p-4">請選擇一個文檔</div>;}return (<div className="p-4 bg-white dark:bg-gray-800 rounded-lg shadow"><h2 className="text-xl font-bold mb-4 text-gray-900 dark:text-white">{document.title}</h2><p>{document.content}</p></div>);
}export default memo(DocumentPreview);
避坑:
- 使用
React.memo
防止不必要的重渲染。 - 結合 React Query 管理異步數據。
5. 性能優化
5.1 分割 Context
將 ThemeContext
、LanguageContext
和 AuthContext
分開,避免單一 Context 導致所有消費者重渲染。
5.2 使用 React.memo
src/components/DocumentList.tsx
和 DocumentPreview.tsx
已使用 memo
包裹,防止 Props 未變化時的重渲染。
5.3 優化狀態更新
src/contexts/AuthContext.ts
(使用 useReducer
):
- 集中管理狀態邏輯,減少直接
setState
調用。 - 確保狀態更新精準,避免全局重渲染。
避坑:
- 僅在必要時更新 Context 值。
- 使用
useMemo
包裝復雜對象:const value = useMemo(() => ({ theme, toggleTheme }), [theme]);
6. 可訪問性(a11y)
src/components/AccessibilityPanel.tsx
:
import { useState } from 'react';
import { useTheme } from '../contexts/ThemeContext';function AccessibilityPanel() {const [highContrast, setHighContrast] = useState(false);const { theme } = useTheme();return (<div className="p-4 bg-white dark:bg-gray-800 rounded-lg shadow"><h2 className="text-xl font-bold mb-4 text-gray-900 dark:text-white">可訪問性設置</h2><label className="flex items-center space-x-2"><inputtype="checkbox"checked={highContrast}onChange={() => setHighContrast(!highContrast)}className="p-2"aria-label="啟用高對比度模式"/><span>高對比度模式</span></label><div className={highContrast ? 'bg-black text-white' : ''}><p aria-live="polite">測試文本: {highContrast ? '高對比度' : '正常'}</p></div></div>);
}export default AccessibilityPanel;
避坑:
- 為動態內容添加
aria-live
屬性。 - 測試屏幕閱讀器(如 NVDA、VoiceOver)。
7. 手機端適配
src/App.tsx
:
import { useState } from 'react';
import { ThemeProvider } from './contexts/ThemeContext';
import { LanguageProvider } from './contexts/LanguageContext';
import { AuthProvider } from './contexts/AuthContext';
import DocumentList from './components/DocumentList';
import DocumentPreview from './components/DocumentPreview';
import ThemeToggle from './components/ThemeToggle';
import LanguageSelector from './components/LanguageSelector';
import AuthPanel from './components/AuthPanel';
import AccessibilityPanel from './components/AccessibilityPanel';
import type { Document } from './types';function App() {const [selectedDoc, setSelectedDoc] = useState<Document | null>(null);return (<ThemeProvider><LanguageProvider><AuthProvider><div className="min-h-screen bg-gray-100 dark:bg-gray-900 p-2 md:p-4"><h1 className="text-2xl md:text-3xl font-bold text-center p-4 text-gray-900 dark:text-white">文檔管理器</h1><div className="grid grid-cols-1 md:grid-cols-2 gap-2 md:gap-4 max-w-5xl mx-auto"><DocumentList onSelect={setSelectedDoc} /><DocumentPreview document={selectedDoc} /><ThemeToggle /><LanguageSelector /><AuthPanel /><AccessibilityPanel /></div></div></AuthProvider></LanguageProvider></ThemeProvider>);
}export default App;
避坑:
- 使用 Tailwind 的響應式類(如
md:
)適配屏幕。 - 確保觸控區域足夠大(至少 48x48 像素)。
8. 部署
8.1 構建項目
npm run build
8.2 部署到 Vercel
- 注冊 Vercel:訪問 Vercel 官網并創建賬號。
- 新建項目:選擇“New Project”。
- 導入倉庫:將項目推送至 GitHub 并導入。
- 配置構建:
- 構建命令:
npm run build
- 輸出目錄:
dist
- 構建命令:
- 部署:點擊“Deploy”.
避坑:
- 確保靜態資源路徑正確(使用相對路徑)。
- 使用 CDN 加速 Tailwind CSS 和其他資源。
常見問題與解決方案
9.1 不必要的重渲染
問題:Context 變化導致所有消費者重渲染。
解決方案:
- 分割 Context(如
ThemeContext
和AuthContext
)。 - 使用
React.memo
包裹消費者組件:export default memo(Component);
9.2 狀態管理復雜性
問題:復雜狀態邏輯導致 Context 難以維護。
解決方案:
- 使用
useReducer
集中管理狀態:const [state, dispatch] = useReducer(authReducer, initialState);
- 結合 React Query 管理異步數據。
9.3 組件耦合
問題:過度依賴 Context 導致組件難以復用。
解決方案:
- 將 Context 邏輯封裝到 Hook:
export function useTheme() {const context = useContext(ThemeContext);if (!context) throw new Error('useTheme 必須在 ThemeProvider 內');return context; }
- 限制 Context 使用范圍。
練習:添加文檔過濾功能
為鞏固所學,設計一個練習:為 DocumentList
添加動態過濾功能,使用 Context 共享過濾狀態。
需求
- 支持按標題過濾文檔。
- 使用 Context 共享過濾狀態。
- 動態更新文檔列表。
實現步驟
1. 創建 Filter Context
src/contexts/FilterContext.ts
:
import { createContext, useContext, useState } from 'react';interface FilterContextType {filter: string;setFilter: (filter: string) => void;
}const FilterContext = createContext<FilterContextType | undefined>(undefined);export function FilterProvider({ children }: { children: React.ReactNode }) {const [filter, setFilter] = useState('');return (<FilterContext.Provider value={{ filter, setFilter }}>{children}</FilterContext.Provider>);
}export function useFilter() {const context = useContext(FilterContext);if (!context) {throw new Error('useFilter 必須在 FilterProvider 內使用');}return context;
}
2. 更新 DocumentList
src/components/DocumentList.tsx
(更新):
import { memo } from 'react';
import { useDocuments } from '../hooks/useDocuments';
import { useLanguage } from '../contexts/LanguageContext';
import { useFilter } from '../contexts/FilterContext';function DocumentList({ onSelect }: { onSelect: (doc: Document) => void }) {const { data: documents, isLoading } = useDocuments();const { t } = useLanguage();const { filter, setFilter } = useFilter();if (isLoading) {return <div className="p-4">加載中...</div>;}const filteredDocs = documents?.filter(doc =>doc.title.toLowerCase().includes(filter.toLowerCase()));return (<div className="p-4 bg-white dark:bg-gray-800 rounded-lg shadow"><h2 className="text-xl font-bold mb-4 text-gray-900 dark:text-white">{t('title')}</h2><inputtype="text"value={filter}onChange={e => setFilter(e.target.value)}className="p-2 border rounded-lg mb-4 w-full"placeholder={t('search')}aria-label={t('search')}/><ul>{filteredDocs?.map(doc => (<likey={doc.id}className="p-2 cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700"onClick={() => onSelect(doc)}role="button"aria-label={`查看文檔 ${doc.title}`}>{doc.title}</li>))}</ul></div>);
}export default memo(DocumentList);
3. 更新 App
src/App.tsx
(更新):
import { FilterProvider } from './contexts/FilterContext';function App() {const [selectedDoc, setSelectedDoc] = useState<Document | null>(null);return (<ThemeProvider><LanguageProvider><AuthProvider><FilterProvider><div className="min-h-screen bg-gray-100 dark:bg-gray-900 p-2 md:p-4"><h1 className="text-2xl md:text-3xl font-bold text-center p-4 text-gray-900 dark:text-white">文檔管理器</h1><div className="grid grid-cols-1 md:grid-cols-2 gap-2 md:gap-4 max-w-5xl mx-auto"><DocumentList onSelect={setSelectedDoc} /><DocumentPreview document={selectedDoc} /><ThemeToggle /><LanguageSelector /><AuthPanel /><AccessibilityPanel /></div></div></FilterProvider></AuthProvider></LanguageProvider></ThemeProvider>);
}
目標:
- 學會使用 Context 共享動態過濾狀態。
- 優化過濾邏輯,減少重渲染。
注意事項
- Context 配置:確保
useContext
在Provider
內部使用。 - 性能優化:分割 Context 和使用
React.memo
。 - 可訪問性:為動態內容添加 ARIA 屬性。
- 學習建議:參考 React 文檔、React Query 文檔 和 Vite 文檔.
結語
通過這個多語言文檔管理應用,您深入掌握了 useContext
的使用方式,從基礎數據共享到復雜狀態管理,結合性能優化和可訪問性實踐。這些技能將幫助您構建高效、可維護的 React 應用,應對復雜業務場景。希望您繼續探索 useContext
的高級應用,如結合 Redux 或服務器端狀態管理,打造卓越的用戶體驗!