استكشف خطاف useReducer في React لإدارة الحالة المعقدة. يغطي هذا الدليل الأنماط المتقدمة وتحسين الأداء وأمثلة واقعية للمطورين حول العالم.
خطاف useReducer في React: إتقان أنماط إدارة الحالة المعقدة
يُعد خطاف useReducer في React أداة قوية لإدارة الحالات المعقدة في تطبيقاتك. على عكس useState، الذي غالبًا ما يكون مناسبًا لتحديثات الحالة الأبسط، يتفوق useReducer عند التعامل مع منطق الحالة المعقد والتحديثات التي تعتمد على الحالة السابقة. سيخوض هذا الدليل الشامل في تعقيدات useReducer، ويستكشف الأنماط المتقدمة، ويقدم أمثلة عملية للمطورين في جميع أنحاء العالم.
فهم أساسيات useReducer
في جوهره، useReducer هو أداة لإدارة الحالة مستوحاة من نمط Redux. يأخذ وسيطين: دالة مخفض (reducer) وحالة أولية. تتعامل دالة الم㬰ففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففف-reducer مع انتقالات الحالة بناءً على الإجراءات المرسلة (dispatched actions). يعزز هذا النمط الكود النظيف، وتصحيح الأخطاء الأسهل، وتحديثات الحالة المتوقعة، وهو أمر بالغ الأهمية للتطبيقات من أي حجم. دعنا نفصل المكونات:
- دالة الم㬰فففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففف-reducer: هذه هي قلب
useReducer. تأخذ الحالة الحالية وكائن الإجراء كمدخلات وتعيد الحالة الجديدة. يحتوي كائن الإجراء عادةً على خاصيةtypeتصف الإجراء الذي سيتم تنفيذه وقد يتضمنpayloadمع بيانات إضافية. - الحالة الأولية: هذه هي نقطة البداية لحالة تطبيقك.
- دالة الإرسال (Dispatch): تسمح لك هذه الدالة بتشغيل تحديثات الحالة عن طريق إرسال الإجراءات. يتم توفير دالة الإرسال بواسطة
useReducer.
إليك مثال بسيط يوضح البنية الأساسية:
import React, { useReducer } from 'react';
// Define the reducer function
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
return state;
}
}
function Counter() {
// Initialize useReducer
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
<button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
</div>
);
}
export default Counter;
في هذا المثال، تتعامل دالة الم㬰ففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففف-reducer مع إجراءات الزيادة والنقصان، محدثةً حالة `count`. تُستخدم دالة dispatch لتشغيل هذه الانتقالات في الحالة.
أنماط useReducer المتقدمة
بينما يكون نمط useReducer الأساسي مباشرًا، إلا أن قوته الحقيقية تظهر عندما تبدأ في التعامل مع منطق حالة أكثر تعقيدًا. إليك بعض الأنماط المتقدمة التي يجب مراعاتها:
1. حمولات الإجراءات المعقدة (Payloads)
لا يجب أن تكون الإجراءات مجرد سلاسل نصية بسيطة مثل 'increment' أو 'decrement'. يمكنها حمل معلومات غنية. يسمح لك استخدام الحمولات (payloads) بتمرير البيانات إلى الم㬰ففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففف-reducer لتحديثات الحالة الأكثر ديناميكية. هذا مفيد للغاية للنماذج، واستدعاءات API، وإدارة القوائم.
function reducer(state, action) {
switch (action.type) {
case 'add_item':
return { ...state, items: [...state.items, action.payload] };
case 'remove_item':
return { ...state, items: state.items.filter(item => item.id !== action.payload) };
default:
return state;
}
}
// Example action dispatch
dispatch({ type: 'add_item', payload: { id: 1, name: 'Item 1' } });
dispatch({ type: 'remove_item', payload: 1 }); // Remove item with id 1
2. استخدام مخفضات متعددة (Reducer Composition)
بالنسبة للتطبيقات الأكبر، يمكن أن تصبح إدارة جميع انتقالات الحالة في مخفض واحد أمرًا صعبًا. يتيح لك تكوين الم㬰ففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففف-reducers بتقسيم إدارة الحالة إلى أجزاء أصغر وأكثر قابلية للإدارة. يمكنك تحقيق ذلك عن طريق دمج عدة مخفضات في مخفض واحد على المستوى الأعلى.
// Individual Reducers
function itemReducer(state, action) {
switch (action.type) {
case 'add_item':
return { ...state, items: [...state.items, action.payload] };
case 'remove_item':
return { ...state, items: state.items.filter(item => item.id !== action.payload) };
default:
return state;
}
}
function filterReducer(state, action) {
switch(action.type) {
case 'SET_FILTER':
return {...state, filter: action.payload}
default:
return state;
}
}
// Combining Reducers
function combinedReducer(state, action) {
return {
items: itemReducer(state.items, action),
filter: filterReducer(state.filter, action)
};
}
// Initial state (Example)
const initialState = {
items: [],
filter: 'all'
};
function App() {
const [state, dispatch] = useReducer(combinedReducer, initialState);
return (
<div>
{/* UI Components that trigger actions on combinedReducer */}
</div>
);
}
3. استخدام `useReducer` مع Context API
يوفر Context API طريقة لتمرير البيانات عبر شجرة المكونات دون الحاجة إلى تمرير الخصائص (props) يدويًا في كل مستوى. عند دمجه مع useReducer، فإنه يخلق حلاً قويًا وفعالًا لإدارة الحالة، وغالبًا ما يُنظر إليه كبديل خفيف لـ Redux. هذا النمط مفيد بشكل استثنائي لإدارة حالة التطبيق العامة.
import React, { createContext, useContext, useReducer } from 'react';
// Create a context for our state
const AppContext = createContext();
// Define the reducer and initial state (as before)
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
return state;
}
}
const initialState = { count: 0 };
// Create a provider component
function AppProvider({ children }) {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<AppContext.Provider value={{ state, dispatch }}>
{children}
</AppContext.Provider>
);
}
// Create a custom hook for easy access
function useAppState() {
return useContext(AppContext);
}
function Counter() {
const { state, dispatch } = useAppState();
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
<button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
</div>
);
}
function App() {
return (
<AppProvider>
<Counter />
</AppProvider>
);
}
هنا، يوفر AppContext الحالة ودالة الإرسال لجميع المكونات الفرعية. يبسط الخطاف المخصص useAppState الوصول إلى السياق.
4. تنفيذ Thunks (الإجراءات غير المتزامنة)
useReducer متزامن بشكل افتراضي. ومع ذلك، في العديد من التطبيقات، ستحتاج إلى إجراء عمليات غير متزامنة، مثل جلب البيانات من واجهة برمجة تطبيقات (API). تمكّن Thunks الإجراءات غير المتزامنة. يمكنك تحقيق ذلك عن طريق إرسال دالة ("thunk") بدلاً من كائن إجراء عادي. ستتلقى الدالة دالة dispatch ويمكنها بعد ذلك إرسال إجراءات متعددة بناءً على نتيجة العملية غير المتزامنة.
function fetchUserData(userId) {
return async (dispatch) => {
dispatch({ type: 'request_user' });
try {
const response = await fetch(`/api/users/${userId}`);
const user = await response.json();
dispatch({ type: 'receive_user', payload: user });
} catch (error) {
dispatch({ type: 'request_user_error', payload: error });
}
};
}
function reducer(state, action) {
switch (action.type) {
case 'request_user':
return { ...state, loading: true, error: null };
case 'receive_user':
return { ...state, loading: false, user: action.payload, error: null };
case 'request_user_error':
return { ...state, loading: false, error: action.payload };
default:
return state;
}
}
function UserProfile({ userId }) {
const [state, dispatch] = useReducer(reducer, { loading: false, user: null, error: null });
React.useEffect(() => {
dispatch(fetchUserData(userId));
}, [userId, dispatch]);
if (state.loading) return <p>Loading...</p>;
if (state.error) return <p>Error: {state.error.message}</p>;
if (!state.user) return null;
return (
<div>
<h2>{state.user.name}</h2>
<p>Email: {state.user.email}</p>
</div>
);
}
يرسل هذا المثال إجراءات لحالات التحميل والنجاح والخطأ أثناء استدعاء API غير المتزامن. قد تحتاج إلى وسيط مثل `redux-thunk` لسيناريوهات أكثر تعقيدًا؛ ومع ذلك، لحالات الاستخدام الأبسط، يعمل هذا النمط بشكل جيد للغاية.
تقنيات تحسين الأداء
يعد تحسين أداء تطبيقات React أمرًا بالغ الأهمية، خاصة عند العمل مع إدارة الحالة المعقدة. إليك بعض التقنيات التي يمكنك استخدامها عند استخدام useReducer:
1. تخزين دالة الإرسال (Memoization of Dispatch Function)
عادةً لا تتغير دالة dispatch من useReducer بين عمليات العرض (renders)، ولكن لا يزال من الممارسات الجيدة تخزينها (memoize) إذا كنت تمررها إلى مكونات فرعية لمنع إعادة العرض غير الضرورية. استخدم React.useCallback لهذا الغرض:
const [state, dispatch] = useReducer(reducer, initialState);
const memoizedDispatch = React.useCallback(dispatch, []); // Memoize dispatch function
يضمن هذا أن دالة dispatch تتغير فقط عندما تتغير التبعيات في مصفوفة التبعية (في هذه الحالة، لا توجد تبعيات، لذلك لن تتغير).
2. تحسين منطق الم㬰فففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففف-Reducer
يتم تنفيذ دالة الم㬰ففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففف-reducer عند كل تحديث للحالة. تأكد من أن الم㬰فففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففف-reducer الخاص بك عالي الأداء عن طريق تقليل الحسابات غير الضرورية وتجنب العمليات المعقدة داخل دالة الم㬰فففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففف-reducer. ضع في اعتبارك ما يلي:
- تحديثات الحالة غير القابلة للتغيير: قم دائمًا بتحديث الحالة بشكل غير قابل للتغيير. استخدم عامل الانتشار (
...) أوObject.assign()لإنشاء كائنات حالة جديدة بدلاً من تعديل الكائنات الحالية مباشرة. هذا مهم لاكتشاف التغيير وتجنب السلوك غير المتوقع. - تجنب النسخ العميق غير الضروري: قم فقط بعمل نسخ عميقة لكائنات الحالة عند الضرورة القصوى. النسخ السطحي (باستخدام عامل الانتشار للكائنات البسيطة) عادة ما يكون كافياً وأقل تكلفة من الناحية الحسابية.
- التهيئة الكسولة (Lazy Initialization): إذا كان حساب الحالة الأولية مكلفًا من الناحية الحسابية، يمكنك استخدام دالة لتهيئة الحالة. سيتم تشغيل هذه الدالة مرة واحدة فقط، أثناء العرض الأولي.
//Lazy initialization
const [state, dispatch] = useReducer(reducer, initialState, (initialArg) => {
//Expensive initialization logic here
return {
...initialArg,
initializedData: 'data'
}
});
3. تخزين الحسابات المعقدة باستخدام `useMemo`
إذا كانت مكوناتك تقوم بعمليات حسابية مكلفة بناءً على الحالة، فاستخدم React.useMemo لتخزين النتيجة. هذا يتجنب إعادة تشغيل الحساب ما لم تتغير التبعيات. هذا أمر بالغ الأهمية للأداء في التطبيقات الكبيرة أو تلك التي تحتوي على منطق معقد.
import React, { useReducer, useMemo } from 'react';
function reducer(state, action) {
// ...
}
function MyComponent() {
const [state, dispatch] = useReducer(reducer, { items: [1, 2, 3, 4, 5] });
const total = useMemo(() => {
console.log('Calculating total...'); // This will only log when the dependencies change
return state.items.reduce((sum, item) => sum + item, 0);
}, [state.items]); // Dependency array: recalculate when items change
return (
<div>
<p>Total: {total}</p>
{/* ... other components ... */}
</div>
);
}
أمثلة واقعية لاستخدام useReducer
دعنا نلقي نظرة على بعض حالات الاستخدام العملية لـ useReducer التي توضح تنوعه. هذه الأمثلة ذات صلة بالمطورين في جميع أنحاء العالم، عبر أنواع مختلفة من المشاريع.
1. إدارة حالة النماذج
النماذج هي مكون شائع في أي تطبيق. يعد useReducer طريقة رائعة للتعامل مع حالة النماذج المعقدة، بما في ذلك حقول الإدخال المتعددة والتحقق من الصحة ومنطق الإرسال. يعزز هذا النمط قابلية الصيانة ويقلل من الكود المتكرر.
import React, { useReducer } from 'react';
function formReducer(state, action) {
switch (action.type) {
case 'change':
return {
...state,
[action.field]: action.value,
};
case 'submit':
//Perform submission logic (API calls, etc.)
return state;
case 'reset':
return {name: '', email: '', message: ''};
default:
return state;
}
}
function ContactForm() {
const [state, dispatch] = useReducer(formReducer, { name: '', email: '', message: '' });
const handleSubmit = (event) => {
event.preventDefault();
dispatch({type: 'submit'});
// Example API Call (Conceptual)
// fetch('/api/contact', { method: 'POST', body: JSON.stringify(state) });
alert('Form submitted (conceptually)!')
dispatch({type: 'reset'});
};
const handleChange = (event) => {
dispatch({ type: 'change', field: event.target.name, value: event.target.value });
};
return (
<form onSubmit={handleSubmit}>
<label htmlFor="name">Name:</label>
<input type="text" id="name" name="name" value={state.name} onChange={handleChange} />
<label htmlFor="email">Email:</label>
<input type="email" id="email" name="email" value={state.email} onChange={handleChange} />
<label htmlFor="message">Message:</label>
<textarea id="message" name="message" value={state.message} onChange={handleChange} />
<button type="submit">Submit</button>
</form>
);
}
export default ContactForm;
يدير هذا المثال بكفاءة حالة حقول النموذج ويتعامل مع كل من تغييرات الإدخال وإرسال النموذج. لاحظ إجراء `reset` لإعادة تعيين النموذج بعد الإرسال الناجح. إنه تطبيق موجز وسهل الفهم.
2. تنفيذ عربة تسوق
تطبيقات التجارة الإلكترونية، الشائعة عالميًا، غالبًا ما تتضمن إدارة عربة تسوق. يعد useReducer مناسبًا تمامًا للتعامل مع تعقيدات إضافة وإزالة وتحديث العناصر في العربة.
function cartReducer(state, action) {
switch (action.type) {
case 'add_item':
const existingItemIndex = state.items.findIndex(item => item.id === action.payload.id);
if (existingItemIndex !== -1) {
// If item exists, increment the quantity
const updatedItems = [...state.items];
updatedItems[existingItemIndex] = { ...updatedItems[existingItemIndex], quantity: updatedItems[existingItemIndex].quantity + 1 };
return { ...state, items: updatedItems };
}
return { ...state, items: [...state.items, { ...action.payload, quantity: 1 }] };
case 'remove_item':
return { ...state, items: state.items.filter(item => item.id !== action.payload) };
case 'update_quantity':
const itemIndex = state.items.findIndex(item => item.id === action.payload.id);
if (itemIndex !== -1) {
const updatedItems = [...state.items];
updatedItems[itemIndex] = { ...updatedItems[itemIndex], quantity: action.payload.quantity };
return { ...state, items: updatedItems };
}
return state;
case 'clear_cart':
return { ...state, items: [] };
default:
return state;
}
}
function ShoppingCart() {
const [state, dispatch] = React.useReducer(cartReducer, { items: [] });
const handleAddItem = (item) => {
dispatch({ type: 'add_item', payload: item });
};
const handleRemoveItem = (itemId) => {
dispatch({ type: 'remove_item', payload: itemId });
};
const handleUpdateQuantity = (itemId, quantity) => {
dispatch({ type: 'update_quantity', payload: {id: itemId, quantity} });
}
// Calculate total
const total = React.useMemo(() => {
return state.items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}, [state.items]);
return (
<div>
<h2>Shopping Cart</h2>
{state.items.length === 0 && <p>Your cart is empty.</p>}
<ul>
{state.items.map(item => (
<li key={item.id}>
{item.name} - ${item.price} x {item.quantity} = ${item.price * item.quantity}
<button onClick={() => handleRemoveItem(item.id)}>Remove</button>
<input type="number" min="1" value={item.quantity} onChange={(e) => handleUpdateQuantity(item.id, parseInt(e.target.value))} />
</li>
))}
</ul>
<p>Total: ${total}</p>
<button onClick={() => dispatch({ type: 'clear_cart' })}>Clear Cart</button>
{/* ... other components ... */}
</div>
);
}
يدير مخفض العربة إضافة وإزالة وتحديث العناصر بكمياتها. يُستخدم خطاف React.useMemo لحساب السعر الإجمالي بكفاءة. هذا مثال شائع وعملي، بغض النظر عن الموقع الجغرافي للمستخدم.
3. تنفيذ تبديل بسيط مع حالة ثابتة
يوضح هذا المثال كيفية دمج useReducer مع التخزين المحلي للحصول على حالة ثابتة. يتوقع المستخدمون غالبًا أن يتم تذكر إعداداتهم. يستخدم هذا النمط التخزين المحلي للمتصفح لحفظ حالة التبديل، حتى بعد تحديث الصفحة. يعمل هذا بشكل جيد للسمات وتفضيلات المستخدم والمزيد.
import React, { useReducer, useEffect } from 'react';
// Reducer function
function toggleReducer(state, action) {
switch (action.type) {
case 'toggle':
return { isOn: !state.isOn };
default:
return state;
}
}
function ToggleWithPersistence() {
// Retrieve the initial state from local storage or default to false
const [state, dispatch] = useReducer(toggleReducer, { isOn: JSON.parse(localStorage.getItem('toggleState')) || false });
// Use useEffect to save the state to local storage whenever it changes
useEffect(() => {
localStorage.setItem('toggleState', JSON.stringify(state.isOn));
}, [state.isOn]);
return (
<div>
<button onClick={() => dispatch({ type: 'toggle' })}>
{state.isOn ? 'On' : 'Off'}
</button>
<p>Toggle is: {state.isOn ? 'On' : 'Off'}</p>
</div>
);
}
export default ToggleWithPersistence;
يقوم هذا المكون البسيط بتبديل حالة ويحفظ الحالة في `localStorage`. يضمن خطاف useEffect حفظ الحالة عند كل تحديث. هذا النمط هو أداة قوية للحفاظ على إعدادات المستخدم عبر الجلسات، وهو أمر مهم عالميًا.
متى تختار useReducer بدلاً من useState
يعتمد القرار بين useReducer و useState على مدى تعقيد حالتك وكيفية تغيرها. إليك دليل لمساعدتك على اتخاذ الخيار الصحيح:
- اختر
useReducerعندما: - يكون منطق حالتك معقدًا ويتضمن قيمًا فرعية متعددة.
- تعتمد الحالة التالية على الحالة السابقة.
- تحتاج إلى إدارة تحديثات الحالة التي تتضمن العديد من الإجراءات.
- تريد مركزية منطق الحالة وتسهيل تصحيح الأخطاء.
- تتوقع الحاجة إلى توسيع نطاق تطبيقك أو إعادة هيكلة إدارة الحالة لاحقًا.
- اختر
useStateعندما: - تكون حالتك بسيطة وتمثل قيمة واحدة.
- تكون تحديثات الحالة مباشرة ولا تعتمد على الحالة السابقة.
- لديك عدد قليل نسبيًا من تحديثات الحالة.
- تريد حلاً سريعًا وسهلاً لإدارة الحالة الأساسية.
كقاعدة عامة، إذا وجدت نفسك تكتب منطقًا معقدًا داخل دوال تحديث useState، فهذا مؤشر جيد على أن useReducer قد يكون مناسبًا بشكل أفضل. غالبًا ما يؤدي خطاف useReducer إلى كود أنظف وأكثر قابلية للصيانة في المواقف التي تحتوي على انتقالات حالة معقدة. يمكن أن يساعد أيضًا في جعل الكود الخاص بك أسهل في اختبار الوحدات، لأنه يوفر آلية متسقة لإجراء تحديثات الحالة.
أفضل الممارسات والاعتبارات
لتحقيق أقصى استفادة من useReducer، ضع هذه الممارسات والاعتبارات في اعتبارك:
- تنظيم الإجراءات: حدد أنواع الإجراءات الخاصة بك كثوابت (على سبيل المثال، `const INCREMENT = 'increment';`) لتجنب الأخطاء المطبعية وجعل الكود الخاص بك أكثر قابلية للصيانة. فكر في استخدام نمط منشئ الإجراءات لتغليف إنشاء الإجراءات.
- التحقق من الأنواع: للمشاريع الأكبر، فكر في استخدام TypeScript لتحديد أنواع حالتك وإجراءاتك ودالة الم㬰ففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففف-reducer. سيساعد هذا في منع الأخطاء وتحسين قابلية قراءة الكود وصيانته.
- الاختبار: اكتب اختبارات الوحدة لدوال الم㬰ففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففف-reducer للتأكد من أنها تعمل بشكل صحيح وتتعامل مع سيناريوهات الإجراءات المختلفة. هذا أمر حاسم لضمان أن تحديثات حالتك متوقعة وموثوقة.
- مراقبة الأداء: استخدم أدوات مطوري المتصفح (مثل React DevTools) أو أدوات مراقبة الأداء لتتبع أداء مكوناتك وتحديد أي اختناقات متعلقة بتحديثات الحالة.
- تصميم شكل الحالة: صمم شكل حالتك بعناية لتجنب التداخل غير الضروري أو التعقيد. ستجعل الحالة المنظمة جيدًا من السهل فهمها وإدارتها.
- التوثيق: وثّق دوال الم㬰فففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففففف-reducer وأنواع الإجراءات بوضوح، خاصة في المشاريع التعاونية. سيساعد هذا المطورين الآخرين على فهم الكود الخاص بك وتسهيل صيانته.
- فكر في البدائل (Redux, Zustand, etc.): للتطبيقات الكبيرة جدًا ذات متطلبات الحالة المعقدة للغاية، أو إذا كان فريقك على دراية بالفعل بـ Redux، فقد ترغب في التفكير في استخدام مكتبة إدارة حالة أكثر شمولاً. ومع ذلك، يوفر
useReducerو Context API حلاً قويًا دون التعقيد الإضافي للمكتبات الخارجية.
الخلاصة
يعد خطاف useReducer في React أداة قوية ومرنة لإدارة الحالة المعقدة في تطبيقاتك. من خلال فهم أساسياته، وإتقان الأنماط المتقدمة، وتنفيذ تقنيات تحسين الأداء، يمكنك بناء مكونات React أكثر قوة وقابلية للصيانة وكفاءة. تذكر أن تخصص نهجك بناءً على احتياجات مشروعك. من إدارة النماذج المعقدة إلى بناء عربات التسوق والتعامل مع التفضيلات الثابتة، يمكّن useReducer المطورين في جميع أنحاء العالم من إنشاء واجهات متطورة وسهلة الاستخدام. كلما تعمقت أكثر في عالم تطوير React، ستثبت إتقان useReducer أنه أصل لا يقدر بثمن في مجموعة أدواتك. تذكر دائمًا إعطاء الأولوية لوضوح الكود وقابليته للصيانة لضمان بقاء تطبيقاتك سهلة الفهم والتطور بمرور الوقت.