React: السياق والحالة العامة
آخر تحديث: 2026-08-26
1. ما ستتعلمه
- createContext و Provider
- useContext: سياق الاستهلاك
- التدريب على السياق مقابل التدريب على العناصر المسرحية
- التداخل متعدد المستويات وتحسين الأداء
- الجمع بين Context و useReducer (إدارة الحالة الخفيفة)
2. قصة حول تغيير السمات
(1) المشكلة: مرور الدعائم عبر 5 مستويات من المكونات
تريد أليس إضافة ميزة التبديل بين «الوضع الداكن» و«الوضع الفاتح» إلى التطبيق. ويجب أن يمر تدفق البيانات عبر خمس طبقات من المكونات:
▶ مثال: استخدام السياق
// Requirements:User switches to dark mode,All child components change accordingly
// Layer 5: innermost Button needs to Know theme
function Button({ theme }) {
return <button style={{
backgroundColor: theme === 'dark' ? '#333' : '#f0f0f0',
color: theme === 'dark' ? 'white' : '#333'
}}>Button</button>
}
// Layer 4
function Sidebar({ theme }) {
return <div><Button theme={theme} /></div>
}
// Layer 3
function MainLayout({ theme }) {
return <div><Sidebar theme={theme} /></div>
}
// Layer 2
function Page({ theme }) {
return <MainLayout theme={theme} />
}
// Layer 1: App has theme state
function App() {
const [theme, setTheme] = useState('light')
return <Page theme={theme} /> // ❌ theme penetrates 5 layers
}
سؤال أليس:
themeمع مرور البيانات من التطبيق عبر كل طبقة وصولاً إلى الزر، فإن الطبقات الثلاث الوسيطة لا تحتاج في الواقع إلى هذه البيانات- إذا أضفنا معلومة أخرى عن المستخدم لنقوم بتمريرها، فستصبح قائمة «props» متضخمة للغاية.
- هذا هو تدريب استخدام الدعائم الكلاسيكي
(2) حل مسألة «السياق»
// 1. Create Context
const ThemeContext = React.createContext('light')
// 2. Provider Provide data
function App() {
const [theme, setTheme] = useState('light')
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<Page />
</ThemeContext.Provider>
)
}
// The middle component is completely unconcerned with theme
function Page() { return <MainLayout /> }
function MainLayout() { return <Sidebar /> }
function Sidebar() { return <Button /> }
// 5. Only the necessary components are loaded Context
function Button() {
const { theme, setTheme } = useContext(ThemeContext)
return (
<button style={{
backgroundColor: theme === 'dark' ? '#333' : '#f0f0f0',
color: theme === 'dark' ? 'white' : '#333'
}}
onClick={() => setTheme(t => t === 'dark' ? 'light' : 'dark')}>
{theme === 'dark' ? '🌙' : '☀️'} Switch Themes
</button>
)
}
المزايا: لم تعد المكونات الوسيطة (Page، MainLayout، Sidebar) بحاجة إلى تمرير خصائص theme؛ حيث يتم تمرير البيانات مباشرةً من «المزود» إلى «المستهلك».
3. المفاهيم الأساسية للسياق
| واجهة برمجة التطبيقات (API) | الدالة | المعلمات/القيم المرجعة |
|---|---|---|
createContext(defaultValue) |
إنشاء حاوية سياق | العودة إلى { Provider, Consumer } |
<Context.Provider value={...}> |
يوفر البيانات للشجرة الفرعية | value: أي نوع؛ يؤدي إلى إعادة عرض «المستهلك» عند تغير قيمته |
useContext(Context) |
استرداد أحدث بيانات المزود | إرجاع value الخاص بالمزود؛ وفي حالة عدم العثور على مزود، يتم إرجاع القيمة الافتراضية |
مكون مخصص useXxx() |
يغلف useContext + فحص الأخطاء | النمط الموصى به: يطلق خطأً واضحًا في حالة عدم وجود Provider |
(1) createContext + Provider + useContext
graph TB
A[createContext<br/>Create a context container] --> B[Provider<br/>Wrapper Component tree<br/>Provide value]
B --> C[Middle Component<br/>Don't care Context]
C --> D[useContext<br/>Consumption Context Data]
style A fill:#1890ff,color:#fff
style B fill:#52c41a,color:#fff
style D fill:#ff6b6b,color:#fff
// 1. Create Context(You can specify a default value)
const UserContext = createContext({ name: 'Guest', role: 'guest' })
const ThemeContext = createContext('light')
// 2. Provider Provide data(The component tree that the wrapper needs to access)
function App() {
const [user, setUser] = useState({ name: 'Alice', role: 'admin' })
return (
<UserContext.Provider value={{ user, setUser }}>
<ThemeContext.Provider value="dark">
<Dashboard />
</ThemeContext.Provider>
</UserContext.Provider>
)
}
// 3. Any child component reads Context
function Dashboard() {
const { user } = useContext(UserContext)
const theme = useContext(ThemeContext)
return <p>{user.name} - Topic:{theme}</p>
}
(2) الحالات التي ينطبق فيها السياق
| مناسب للسياق | غير مناسب للسياق |
|---|---|
| السمة (الوضع الداكن) | معلمات تكوين المكونات (باستخدام Props) |
| معلومات تسجيل دخول المستخدم | قيم الإدخال في النموذج (باستخدام الحالة المحلية) |
| اللغة/التدويل | بيانات واجهة برمجة التطبيقات (باستخدام TanStack Query) |
| حالة المسار | حالة الرسوم المتحركة (تديرها مكتبة الرسوم المتحركة) |
| الإعدادات العامة | التمرير البسيط للخصائص من العنصر الأصلي إلى العنصر التابع (باستخدام الخصائص مباشرةً) |
القاعدة: استخدم «Context» فقط عندما تكون هناك حاجة لمشاركة البيانات عبر «مستويات متعددة ومكونات متعددة». أما إذا كنت تقوم ببساطة بتمرير القيم بين المكونات الأم والمكونات التابعة، فإن «Props» تكفي.
4. تداخل السياقات وتقسيم المكونات إلى مزودات
(1) تداخل السياقات متعدد المستويات
عادةً ما تتضمن المشاريع الواقعية سياقات متعددة:
function App() {
return (
<AuthProvider>
<ThemeProvider>
<I18nProvider>
<NotificationProvider>
<MainApp />
</NotificationProvider>
</I18nProvider>
</ThemeProvider>
</AuthProvider>
)
}
لتجنب «جحيم التداخل»، يمكنك إنشاء مكون مركب AppProviders:
function AppProviders({ children }) {
return (
<AuthProvider>
<ThemeProvider>
<I18nProvider>
<NotificationProvider>
{children}
</NotificationProvider>
</I18nProvider>
</ThemeProvider>
</AuthProvider>
)
}
function App() {
return (
<AppProviders>
<MainApp />
</AppProviders>
)
}
(2) ربط مزود مخصص
// ============================================
// Example:Custom ThemeProvider + useTheme Hook
// ============================================
const ThemeContext = React.createContext(null)
function ThemeProvider({ children }) {
const [theme, setTheme] = React.useState(() => {
// from localStorage Read the initial topic
return localStorage.getItem('theme') || 'light'
})
function toggleTheme() {
setTheme(prev => {
const next = prev === 'light' ? 'dark' : 'light'
localStorage.setItem('theme', next) // Persistence
return next
})
}
const value = React.useMemo(
() => ({ theme, toggleTheme }),
[theme]
)
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
)
}
// Custom Hook:Packaging useContext + Error Checking
function useTheme() {
const context = useContext(ThemeContext)
if (!context) {
throw new Error('useTheme Must be ThemeProvider For internal use only')
}
return context
}
// Usage
function ThemedButton() {
const { theme, toggleTheme } = useTheme()
return (
<button onClick={toggleTheme}
style={{
backgroundColor: theme === 'dark' ? '#333' : '#f0f0f0',
color: theme === 'dark' ? 'white' : '#333',
padding: '10px 20px',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}}>
Current Topic:{theme === 'dark' ? '🌙' : '☀️'} Click to toggle
</button>
)
}
5. تحسين الأداء حسب السياق
عندما تتغير قيمة «السياق»، يتم إعادة عرض جميع المكونات التي تستخدم هذا «السياق». وهذا هو أكبر عائق يواجه أداء «السياقات».
| طريقة التحسين | المبدأ | السيناريوهات القابلة للتطبيق |
|---|---|---|
useMemo قيمة ثابتة |
تجنب إنشاء مراجع كائنات جديدة عند كل عملية عرض | عندما تحتوي قيمة المزود على دالة أو كائن |
| التقسيم حسب السياق | تُخصص البيانات «المتغيرة» و«الثابتة» لسياقات مختلفة | لا يحتاج بعض المستهلكين سوى إلى بيانات مستقرة |
React.memo مكون Consumer المُغلف |
تخطي إعادة العرض عندما لا تتغير الخصائص | عندما يتلقى مكون Consumer خصائص أخرى |
| الاشتراك الانتقائي | استخدم أداة التحديد لاسترجاع الشرائح التي تحتاجها فقط | تنفيذ تحديثات دقيقة باستخدام Zustand/Redux |
// ❌ Question:Every time App Rendering,value They're all new partners. → All Consumer All are heavily rendered
function App() {
const [count, setCount] = useState(0)
return (
<ThemeContext.Provider value={{ theme: 'dark', setCount }}>
<ExpensiveTree />
</ThemeContext.Provider>
)
}
// ✅ Fix 1:use useMemo Stable value Quote
function App() {
const [count, setCount] = useState(0)
const themeValue = useMemo(
() => ({ theme: 'dark', toggleTheme: () => setCount(c => c + 1) }),
[]
)
return (
<ThemeContext.Provider value={themeValue}>
<ExpensiveTree />
</ThemeContext.Provider>
)
}
// ✅ Fix 2:separate "unchanged" and "changed" data into different Context
const SettingsContext = createContext({ theme: 'light', language: 'zh' })
const CountContext = createContext(0)
function App() {
const [count, setCount] = useState(0)
return (
<SettingsContext.Provider value={settingsValue}> {/* useMemo Stable */}
<CountContext.Provider value={count}>
<ExpensiveTree />
</CountContext.Provider>
</SettingsContext.Provider>
)
}
// Only when used CountContext The component will be count Re-render on changes
// SettingsContext Consumers will not be affected
6. Context + useReducer: إنشاء حالة عامة خفيفة الوزن
من خلال الجمع بين «الانتشار عبر المستويات» في Context و«إدارة الحالة المعقدة» في useReducer، يمكنك إنشاء حل خفيف الوزن لإدارة الحالة العامة:
// ============================================
// Example:Global Notification System(Context + useReducer)
// ============================================
// ---- 1. Create Context ----
const NotificationContext = createContext(null)
// ---- 2. Definition reducer ----
function notificationReducer(state, action) {
switch (action.type) {
case 'ADD':
return [...state, {
id: Date.now(),
message: action.message,
type: action.notifType || 'info',
read: false
}]
case 'MARK_READ':
return state.map(n =>
n.id === action.id ? { ...n, read: true } : n
)
case 'REMOVE':
return state.filter(n => n.id !== action.id)
case 'CLEAR_ALL':
return []
default:
return state
}
}
// ---- 3. Provider Components ----
function NotificationProvider({ children }) {
const [notifications, dispatch] = useReducer(notificationReducer, [])
// use useMemo Stable value
const value = useMemo(() => ({ notifications, dispatch }), [notifications])
return (
<NotificationContext.Provider value={value}>
{children}
</NotificationContext.Provider>
)
}
// ---- 4. Custom Hook ----
function useNotification() {
const context = useContext(NotificationContext)
if (!context) throw new Error('useNotification Must be NotificationProvider For internal use')
return context
}
// ---- 5. Using the notification component ----
function NotificationBell() {
const { notifications, dispatch } = useNotification()
const unread = notifications.filter(n => !n.read).length
return (
<div style={{ position: 'relative', cursor: 'pointer' }}>
🔔
{unread > 0 && (
<span style={{
position: 'absolute', top: '-8px', right: '-8px',
backgroundColor: '#ff4d4f', color: 'white',
borderRadius: '50%', width: '18px', height: '18px',
fontSize: '12px', display: 'flex', alignItems: 'center',
justifyContent: 'center'
}}>
{unread}
</span>
)}
</div>
)
}
function NotificationList() {
const { notifications, dispatch } = useNotification()
if (notifications.length === 0) {
return <p style={{ color: '#999', textAlign: 'center' }}>No notice at this time</p>
}
return (
<div style={{ maxWidth: '400px' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '8px' }}>
<strong>Notice({notifications.length})</strong>
<button onClick={() => dispatch({ type: 'CLEAR_ALL' })} style={smallBtn}>Clear All</button>
</div>
{notifications.map(n => (
<div key={n.id} style={{
padding: '8px', marginBottom: '4px',
backgroundColor: n.read ? '#fafafa' : '#e6f7ff',
borderRadius: '4px', cursor: 'pointer',
display: 'flex', justifyContent: 'space-between',
alignItems: 'center'
}}
onClick={() => dispatch({ type: 'MARK_READ', id: n.id })}>
<span>
{n.type === 'error' ? '❌' : n.type === 'success' ? '✅' : 'ℹ️'}
{n.message}
</span>
<button onClick={e => { e.stopPropagation(); dispatch({ type: 'REMOVE', id: n.id }) }}
style={{ ...smallBtn, color: '#ff4d4f' }}>✕</button>
</div>
))}
</div>
)
}
// ---- The component that sends notifications ----
function NotificationSender() {
const { dispatch } = useNotification()
const [message, setMessage] = useState('')
function send(type) {
if (!message.trim()) return
dispatch({ type: 'ADD', message: message.trim(), notifType: type })
setMessage('')
}
return (
<div>
<input value={message} onChange={e => setMessage(e.target.value)}
placeholder="Notice Content" style={{ width: '100%', padding: '8px', marginBottom: '8px', border: '1px solid #d9d9d9', borderRadius: '4px' }} />
<div style={{ display: 'flex', gap: '4px' }}>
<button onClick={() => send('info')} style={typeBtn('#1890ff')}>ℹ️ info</button>
<button onClick={() => send('success')} style={typeBtn('#52c41a')}>✅ Success</button>
<button onClick={() => send('error')} style={typeBtn('#ff4d4f')}>❌ Error</button>
</div>
</div>
)
}
const smallBtn = { padding: '2px 8px', fontSize: '12px', border: 'none', backgroundColor: 'transparent', cursor: 'pointer' }
const typeBtn = (color) => ({ padding: '6px 12px', backgroundColor: color, color: 'white', border: 'none', borderRadius: '4px', cursor: 'pointer' })
// ---- 6. Assembly ----
function NotificationApp() {
return (
<NotificationProvider>
<div style={{ maxWidth: '500px', margin: '0 auto' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<h2>🔔 Notification System</h2>
<NotificationBell />
</div>
<NotificationSender />
<hr style={{ margin: '16px 0' }} />
<h3>List of Notices</h3>
<NotificationList />
</div>
</NotificationProvider>
)
}
النتيجة المتوقعة: نظام إشعارات متكامل. يظهر في الجزء العلوي جرس إشعارات (يعرض عدد الإشعارات غير المقروءة)؛ وفي الوسط، يمكن للمستخدمين إرسال ثلاثة أنواع من الإشعارات: إشعارات المعلومات، وإشعارات النجاح، وإشعارات الخطأ؛ وفي الجزء السفلي، تُعرض قائمة بالإشعارات، حيث يمكن وضع علامة «مقروء» على كل إشعار أو حذفه.
▶ المثال 2: إدارة حالة مصادقة المستخدم (السياق + ربط مخصص)
// ============================================
// Example:User Authentication System——Context + Custom Hook Best Practices
// Features:Admin Login/Logged Out,Provide user information globally
// ============================================
import { createContext, useContext, useState, useCallback, useMemo } from 'react'
// ---- 1. Create Context ----
const AuthContext = createContext(null)
// ---- 2. Provider Components ----
function AuthProvider({ children }) {
const [user, setUser] = useState(null)
const [loading, setLoading] = useState(false)
const login = useCallback(async (username, password) => {
setLoading(true)
try {
// Simulate a login request
await new Promise(resolve => setTimeout(resolve, 800))
if (username && password) {
const loggedInUser = {
id: 1,
name: username,
email: `${username}@example.com`,
avatar: 'https://i.pravatar.cc/40',
role: username === 'admin' ? 'Administrator' : 'Regular User'
}
setUser(loggedInUser)
localStorage.setItem('user', JSON.stringify(loggedInUser))
return { success: true }
}
return { success: false, error: 'Incorrect username or password' }
} finally {
setLoading(false)
}
}, [])
const logout = useCallback(() => {
setUser(null)
localStorage.removeItem('user')
}, [])
// use useMemo Stable value Quote,Avoid Provider Render all child components again
const value = useMemo(() => ({
user,
loading,
isAuthenticated: !!user,
login,
logout
}), [user, loading, login, logout])
return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
)
}
// ---- 3. Custom Hook + Error Checking ----
function useAuth() {
const context = useContext(AuthContext)
if (!context) {
throw new Error('useAuth Must be AuthProvider For internal use only')
}
return context
}
// ---- 4. Use Certified Components ----
function LoginForm() {
const { login, loading } = useAuth()
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [error, setError] = useState('')
async function handleSubmit(e) {
e.preventDefault()
setError('')
const result = await login(username, password)
if (!result.success) {
setError(result.error)
}
}
return (
<form onSubmit={handleSubmit} style={{ maxWidth: '400px', margin: '0 auto' }}>
<h2>🔐 Log In</h2>
{error && <p style={{ color: '#ff4d4f', fontSize: '14px' }}>{error}</p>}
<input value={username} onChange={e => setUsername(e.target.value)}
placeholder="Username(Input admin Experience the Role of an Administrator)"
style={{ width: '100%', padding: '8px', marginBottom: '8px', border: '1px solid #d9d9d9', borderRadius: '4px' }} />
<input type="password" value={password} onChange={e => setPassword(e.target.value)}
placeholder="Password"
style={{ width: '100%', padding: '8px', marginBottom: '8px', border: '1px solid #d9d9d9', borderRadius: '4px' }} />
<button type="submit" disabled={loading}
style={{ width: '100%', padding: '10px', backgroundColor: '#1890ff', color: 'white', border: 'none', borderRadius: '4px', cursor: loading ? 'not-allowed' : 'pointer' }}>
{loading ? 'Logging in......' : 'Log In'}
</button>
</form>
)
}
function UserDashboard() {
const { user, logout } = useAuth()
return (
<div style={{ maxWidth: '400px', margin: '0 auto' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '12px', marginBottom: '16px' }}>
<img src={user.avatar} alt={user.name}
style={{ width: '40px', height: '40px', borderRadius: '50%' }} />
<div>
<h3 style={{ margin: 0 }}>{user.name}</h3>
<p style={{ margin: '4px 0 0 0', color: '#666', fontSize: '13px' }}>
{user.email} · {user.role}
</p>
</div>
</div>
<button onClick={logout}
style={{ padding: '8px 16px', backgroundColor: '#ff4d4f', color: 'white', border: 'none', borderRadius: '4px', cursor: 'pointer' }}>
Log Out
</button>
</div>
)
}
// ---- 5. Top-level component ----
function AuthApp() {
const { isAuthenticated } = useAuth()
return isAuthenticated ? <UserDashboard /> : <LoginForm />
}
// ---- 6. Entrance ----
function App() {
return (
<AuthProvider>
<div style={{ maxWidth: '600px', margin: '0 auto', padding: '20px' }}>
<AuthApp />
</div>
</AuthProvider>
)
}
▶ المثال 3: دمج طبقات مزودين متعددة مع التوطين الدولي (i18n)
const I18nContext = createContext(null)
function I18nProvider({ children }) {
const [locale, setLocale] = useState('zh')
const translations = {
zh: { title: 'App Title', greeting: 'Hello', logout: 'Exit' },
en: { title: 'App Title', greeting: 'Hello', logout: 'Logout' },
}
const t = useCallback((key) => translations[locale][key] || key, [locale])
const value = useMemo(() => ({ locale, setLocale, t }), [locale, t])
return <I18nContext.Provider value={value}>{children}</I18nContext.Provider>
}
function useI18n() {
const ctx = useContext(I18nContext)
if (!ctx) throw new Error('useI18n must be inside I18nProvider')
return ctx
}
function LocaleSwitcher() {
const { locale, setLocale } = useI18n()
return (
<select value={locale} onChange={e => setLocale(e.target.value)}
style={{ padding: '4px 8px', borderRadius: 4 }}>
<option value="zh">Chinese</option>
<option value="en">English</option>
</select>
)
}
function Greeting() {
const { t } = useI18n()
return <h2>{t('greeting')}</h2>
}
function I18nApp() {
return (
<I18nProvider>
<div style={{ maxWidth: 400, margin: '0 auto' }}>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<Greeting />
<LocaleSwitcher />
</div>
</div>
</I18nProvider>
)
}
▶ المثال 4: تقسيم السياق لتحسين الأداء
const UserStateContext = createContext(null)
const UserDispatchContext = createContext(null)
function UserProvider({ children }) {
const [user, setUser] = useState(null)
const [loading, setLoading] = useState(false)
const dispatch = useMemo(() => ({
login: async (name) => {
setLoading(true)
await new Promise(r => setTimeout(r, 500))
setUser({ name, role: name === 'admin' ? 'Admin' : 'User' })
setLoading(false)
},
logout: () => setUser(null),
}), [])
const stateValue = useMemo(() => ({ user, loading }), [user, loading])
return (
<UserDispatchContext.Provider value={dispatch}>
<UserStateContext.Provider value={stateValue}>
{children}
</UserStateContext.Provider>
</UserDispatchContext.Provider>
)
}
function useUserState() { return useContext(UserStateContext) }
function useUserDispatch() { return useContext(UserDispatchContext) }
function LoginButton() {
const { login } = useUserDispatch()
const { loading } = useUserState()
return <button disabled={loading} onClick={() => login('Alice')}>Login</button>
}
function UserInfo() {
const { user } = useUserState()
if (!user) return <p>Not logged in</p>
return <p>Logged in as {user.name} ({user.role})</p>
}
function SplitContextApp() {
return (
<UserProvider>
<div style={{ maxWidth: 400, margin: '0 auto' }}>
<UserInfo />
<LoginButton />
</div>
</UserProvider>
)
}
▶ المثال 5: مثال كامل على استخدام useContext كبديل عن تدريبات «Props Drilling»
const SettingsContext = createContext(null)
function SettingsProvider({ children }) {
const [settings, setSettings] = useState({
theme: 'light', fontSize: 14, sidebarCollapsed: false,
})
const updateSetting = useCallback((key, value) => {
setSettings(prev => ({ ...prev, [key]: value }))
}, [])
const value = useMemo(() => ({ settings, updateSetting }), [settings, updateSetting])
return <SettingsContext.Provider value={value}>{children}</SettingsContext.Provider>
}
function useSettings() {
const ctx = useContext(SettingsContext)
if (!ctx) throw new Error('useSettings must be inside SettingsProvider')
return ctx
}
function Toolbar() {
const { settings, updateSetting } = useSettings()
return (
<div style={{ padding: 8, background: settings.theme === 'dark' ? '#333' : '#f0f0f0', display: 'flex', gap: 8, alignItems: 'center' }}>
<button onClick={() => updateSetting('theme', settings.theme === 'dark' ? 'light' : 'dark')}>
{settings.theme === 'dark' ? '☀️' : '🌙'}
</button>
<button onClick={() => updateSetting('sidebarCollapsed', !settings.sidebarCollapsed)}>
{settings.sidebarCollapsed ? '▶' : '◀'} Sidebar
</button>
<select value={settings.fontSize} onChange={e => updateSetting('fontSize', Number(e.target.value))}>
<option value={12}>12px</option>
<option value={14}>14px</option>
<option value={16}>16px</option>
<option value={18}>18px</option>
</select>
</div>
)
}
function Content() {
const { settings } = useSettings()
return (
<div style={{ fontSize: settings.fontSize, color: settings.theme === 'dark' ? '#eee' : '#333',
background: settings.theme === 'dark' ? '#1a1a1a' : '#fff', padding: 16 }}>
<p>Current settings:</p>
<ul>
<li>Theme: {settings.theme}</li>
<li>Font size: {settings.fontSize}px</li>
<li>Sidebar: {settings.sidebarCollapsed ? 'collapsed' : 'expanded'}</li>
</ul>
</div>
)
}
function SettingsApp() {
return (
<SettingsProvider>
<div style={{ maxWidth: 600, margin: '0 auto' }}>
<Toolbar />
<Content />
</div>
</SettingsProvider>
)
}
❓ أسئلة شائعة
value الخاص بـ Context يمثل كائنًا جديدًا في كل مرة، مما يتسبب في إعادة عرض جميع Consumer؟useMemo لتثبيت مرجع value: const value = useMemo(() => ({ theme, toggleTheme }), [theme]). يمكنك أيضًا فصل البيانات «الثابتة» و«المتغيرة» إلى Context مختلفة. إذا استمرت مشكلات الأداء بعد التحسين، ففكر في استخدام Zustand (الذي يدعم التحديثات الدقيقة ولن يؤدي إلى إعادة عرض المكونات غير ذات الصلة).AppProviders المذكور سابقًا) وتغليف التداخل؛ ② فكر فيما إذا كنت بحاجة فعلاً إلى هذا العدد الكبير من السياقات — فبعض البيانات «العامة» هي في الواقع مجرد بيانات «عامة محلياً» (مثل إعدادات صفحة معينة) ولا تحتاج إلى وضعها في سياق المستوى الأعلى للتطبيق.context قيمة فارغة في خطاف useXxx المخصص؟Provider قبل استخدام useContext، فسيكون context فارغًا (إذا لم يتم تمرير قيمة افتراضية إلى createContext) أو سيتسبب في أخطاء يصعب تصحيحها. إن إظهار رسالة خطأ واضحة في وقت مبكر يعد أكثر سهولة للمستخدم بكثير من أن يرى المستخدم عبارة «لا يمكن قراءة خاصية null» في وحدة التحكم بالمتصفح.📖 ملخص
createContext→.Provider→useContext3 خطوات لاستخدام Context- يحل Context مشكلة Props Drilling، لكنه لا يحل مشكلة «إدارة الحالة»
- مشكلة في الأداء: تؤدي التغييرات في قيمة السياق إلى إعادة عرض جميع العناصر المستهلكة؛ قم بالتحسين باستخدام
useMemo - يُعد تخصيص الـ
useXxx+ فحص الأخطاء من أفضل الممارسات الموصى بها - يمكن أن يُستخدم
Context + useReducerكحل خفيف الوزن لإدارة الحالة العالمية، ولكن في الحالات المعقدة، لا يزال من الأفضل استخدام Zustand أو Redux.
📝 تمارين
- المشكلة الأساسية (صعوبة ⭐): قم بإنشاء ربط
LocaleProvider+useLocaleيوفر طريقتيlocale(zh/en) وtoggleLocale()لتمكين التبديل بين اللغتين الصينية والإنجليزية. - تمرين متقدم (مستوى الصعوبة ⭐⭐): قم بإنشاء هوك
AuthProvider+useAuthيوفر الطرقuserوlogin(username, password)وlogout()لإدارة حالة مصادقة المستخدم. - التحدي (الصعوبة: ⭐⭐⭐): أنشئ هوكًا باستخدام
CartProvider+useCartواستخدم Context + useReducer لتنفيذ عربة تسوق مزودة بوظائف لإضافة العناصر وإزالتها وتغيير الكميات ومسح محتويات العربة. استخدمuseMemoلتحسين الكود وتجنب إعادة العرض غير الضرورية.