React
قراءة في 3 دقائقمقارنة إدارة الحالة: Redux Toolkit مقابل Zustand وJotai
ثلاثة نماذج ذهنية مختلفة للمشكلة نفسها، مع مثال عملي لكل منها، وجدول مقارنة جنبًا إلى جنب، وإجابة مباشرة عن سؤال «أيها أستخدم فعلًا؟»

سؤال «أي مدير حالة أستخدم؟» هو في الحقيقة ثلاثة أسئلة منفصلة تختبئ تحت مظلة واحدة: كم من الشيفرة التمهيدية (boilerplate) أنا مستعد لكتابته؟ هل أحتاج إلى تصحيح الأخطاء بالتنقل عبر الزمن؟ وهل حالتي عامة فعلًا أم مشتركة فقط بين بضعة مكوّنات متجاورة؟ تجيب Redux Toolkit وZustand وJotai عن هذه الأسئلة بطرق مختلفة جدًا.
Redux Toolkit — مخزن مركزي واحد وكل شيء صريح
Redux Toolkit (اختصارًا RTK) هو الطريقة الحديثة والمُوجِّهة لكتابة Redux: شيفرة تمهيدية أقل من Redux الكلاسيكي، لكن بالفكرة الأساسية نفسها — مخزن واحد، وإجراءات (actions) تصف ما حدث، ومختزِلات (reducers) تقرر كيف تتغير الحالة.
import { createSlice } from "@reduxjs/toolkit";
const counterSlice = createSlice({
name: "counter",
initialState: { value: 0 },
reducers: {
increment: (state) => { state.value += 1; }, // Immer runs under the hood — this "mutation" is safe
incrementBy: (state, action: { payload: number }) => { state.value += action.payload; },
},
});
export const { increment, incrementBy } = counterSlice.actions;
export default counterSlice.reducer;
// inside a component
const count = useSelector((state: RootState) => state.counter.value);
const dispatch = useDispatch();
dispatch(increment());Zustand — مخزن ليس سوى hook
يتخلى Zustand عن الشكليات. المخزن مجرد دالة؛ وتشترك المكوّنات فقط في الجزء الذي تقرؤه، فتبقى إعادة التصيير في حدّها الأدنى دون memoization إضافي.
import { create } from "zustand";
const useCounterStore = create<{ value: number; increment: () => void }>((set) => ({
value: 0,
increment: () => set((s) => ({ value: s.value + 1 })),
}));
// inside a component — only re-renders when value changes
const count = useCounterStore((s) => s.value);
const increment = useCounterStore((s) => s.increment);Jotai — الحالة على شكل ذرّات (atoms) لا كائن ضخم واحد
يقلب Jotai النموذج: بدل مخزن واحد يحمل كل شيء، تعيش الحالة في ذرّات صغيرة مستقلة تتركّب معًا. لا حاجة إلى selectors — فالمكوّن الذي يقرأ ذرّة لا يُعاد تصييره إلا عندما تتغير تلك الذرّة.
import { atom, useAtom } from "jotai";
const countAtom = atom(0);
const doubledAtom = atom((get) => get(countAtom) * 2); // derived atom, recomputed automatically
// inside a component
const [count, setCount] = useAtom(countAtom);
const [doubled] = useAtom(doubledAtom);مقارنة جنبًا إلى جنب
إذن أيها فعلًا؟
- اختر Redux Toolkit عندما تكون الحالة عابرة للأجزاء حقًا، والفريق أكبر من شخصين أو ثلاثة، وتريد أيضًا استفادة من التخزين المؤقت في RTK Query لبيانات الخادم.
- اختر Zustand عندما تريد حالة عامة دون شكليات — معظم المشاريع الشخصية والتطبيقات المتوسطة تنتهي هنا.
- اختر Jotai عندما تكون حالتك بطبيعتها شبكة من قيم صغيرة مشتقة — النماذج متعددة الخطوات، ولوحات المرشحات، وأي مكان تعتمد فيه الحقول على بعضها.
- وقبل اللجوء إلى أيٍّ منها: تحقق هل الحالة عامة فعلًا، أم أنها مجرد prop أو context يُمرَّر بضعة مستويات — ليس كل شيء يحتاج إلى مخزن.
أفضل مدير حالة هو أصغر واحد يجعل مشكلتك الحالية سهلة الفهم. ولا بأس — بل هو أمر جيد — أن تستخدم أدوات مختلفة لأجزاء مختلفة من التطبيق نفسه.