بس بدل “عداد” نخليه رصيد بنك فيه:
deposit→ إيداع (زيادة الرصيد)withdraw→ سحب (نقص الرصيد)- مع قيمة ابتدائية 100
بنفس نمط المثال السابق: Slice + Store + Component ❤️
1️⃣ ملف الـ Slice: bankSlice.js
// 1) نستورد createSlice من Redux Toolkit
import { createSlice } from '@reduxjs/toolkit';
// 2) الحالة الابتدائية
const initialState = {
balance: 100, // الرصيد يبدأ بـ 100
};
// 3) إنشاء الـ slice
const bankSlice = createSlice({
name: 'bank',
initialState,
reducers: {
// إيداع
deposit: (state, action) => {
// action.payload = المبلغ المودَع
state.balance = state.balance + action.payload;
},
// سحب
withdraw: (state, action) => {
// نتأكد ما ينزل أقل من 0 (مجرد مثال)
const newBalance = state.balance - action.payload;
state.balance = newBalance < 0 ? 0 : newBalance;
},
// إعادة تعيين الرصيد (اختياري)
resetBalance: (state) => {
state.balance = 100;
},
},
});
// 4) نصدر الـ actions
export const { deposit, withdraw, resetBalance } = bankSlice.actions;
// 5) نصدر الـ reducer كـ default
export default bankSlice.reducer;JavaScriptلاحظ:
نفس الفكرة بالضبط مثلcounterSlice
لكن الحقل اسمهbalanceبدلvalue
والدوال:depositوwithdrawبدلincrementوdecrement.
2️⃣ ملف الـ Store: store.js
import { configureStore } from '@reduxjs/toolkit';
// نستورد الـ reducer من الـ slice
import bankReducer from './bankSlice';
// ننشئ الـ store
export const store = configureStore({
reducer: {
// اسم الجزء في الـ state: bank
bank: bankReducer,
},
});JavaScriptالـ state سيبدو هكذا:
{
bank: {
balance: 100
}
}
JavaScript3️⃣ كمبوننت بسيط: Bank.jsx
import React, { useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
// نستورد الـ actions
import { deposit, withdraw, resetBalance } from './bankSlice';
function Bank() {
// نقرأ الرصيد الحالي من الـ state
const balance = useSelector((state) => state.bank.balance);
// dispatch لإرسال actions
const dispatch = useDispatch();
// حالة محلية لقيمة المبلغ
const [amount, setAmount] = useState(0);
return (
<div>
<h1>الرصيد الحالي: {balance} ريال</h1>
<input
type="number"
value={amount}
onChange={(e) => setAmount(Number(e.target.value))}
placeholder="ادخل المبلغ"
/>
{/* إيداع */}
<button onClick={() => dispatch(deposit(amount))}>
إيداع
</button>
{/* سحب */}
<button onClick={() => dispatch(withdraw(amount))}>
سحب
</button>
{/* إعادة الرصيد للقيمة الابتدائية */}
<button onClick={() => dispatch(resetBalance())}>
إعادة تعيين الرصيد
</button>
</div>
);
}
export default Bank;
JavaScriptربط الـ
storeمعProviderفيmain.jsx/index.jsx
هو نفسه الذي شرحناه في مثال العداد، ما تغيّر شيء 👍
الفكرة العامة (نفس نمط العداد):
- عند الضغط على زر إيداع:
dispatch(deposit(amount))- يروح للـ reducer → يزيد
balanceبالمبلغ.
- عند الضغط على سحب:
dispatch(withdraw(amount))- ينقص
balanceبالمبلغ (مع حماية بسيطة ألا ينزل تحت 0).
- عند الضغط على إعادة تعيين:
- يعيد
balanceإلى 100.
- يعيد
1️⃣ ما هي الـ action في Redux؟
أي Action في Redux هو كائن (object) شكله تقريبًا:
{
type: 'bank/deposit',
payload: 100
}
JavaScripttype→ اسم الحدث (إيداع، سحب، حذف، …).payload→ البيانات اللي نرسلها مع الحدث (المبلغ، المنتج، المستخدم، …).
في Redux Toolkit لما نستخدم createSlice، هي تلقائيًا تبني لك:
- الـ action type
- و دالة action creator اللي ترجع هذا الكائن.
2️⃣ مثال البنك: ماذا يحدث عند dispatch(deposit(amount))؟
في الكمبوننت كتبنا:
dispatch(deposit(amount));
JavaScriptخلّينا نفترض أن:
amount = 50;
JavaScriptدالة deposit (اللي أنشأتها Redux Toolkit لنا من الـ slice) ترجع object مثل:
deposit(50);
// يساوي تقريبًا:
{
type: 'bank/deposit',
payload: 50
}JavaScriptثم dispatch يرسل هذا الـ object للـ reducer.
3️⃣ ماذا تعني action.payload في الـ reducer؟
في الـ slice كتبنا:
deposit: (state, action) => {
state.balance = state.balance + action.payload;
}
JavaScriptهنا:
- البراميتر الأول:
state→ يمثل جزء الـ state الخاص بهذا الـ slice (مثلًا{ balance: 100 }). - البراميتر الثاني:
action→ هو نفس الـ object اللي تكلمنا عنه فوق:
{
type: 'bank/deposit',
payload: 50
}
إذن:
action.type='bank/deposit'action.payload=50← هذا هو المبلغ اللي أرسلناه من الكمبوننت
فجملة:
state.balance = state.balance + action.payload;JavaScriptتساوي:
state.balance = state.balance + 50;JavaScript4️⃣ هل يمكن تغيير اسم action أو payload؟
✅ أولًا: اسم البراميتر action
نعم، تقدر تغيّر اسم البراميتر اللي في الدالة:
deposit: (state, action) => { ... }
JavaScriptتقدر تكتب:
deposit: (state, myAction) => {
state.balance = state.balance + myAction.payload;
}
JavaScriptأو حتى:
deposit: (state, event) => {
state.balance = state.balance + event.payload;
}
الاسم هنا اختياري تمامًا لأنّه مجرد اسم متغيّر في الدالة.
✅ ثانيًا: اسم الخاصية payload
هنا مهم نركّز…
في Redux Toolkit، الـ action اللي تُنشئها createSlice يكون شكلها دائمًا:
{ type: 'something', payload: القيمة_التي_مررتها }JavaScriptاسم الخاصية payload ثابت حسب الاتفاق (Convention).
لكن داخل الدالة تقدر تعمل تفكيك (destructuring) وتغيّر الاسم محليًا:
deposit: (state, action) => {
const amount = action.payload;
state.balance += amount;
}JavaScriptأو بطريقة أقصر:
deposit: (state, { payload }) => {<br> state.balance += payload;<br>}JavaScriptأو:
deposit: (state, { payload: amount }) => {
state.balance += amount;
}JavaScriptهنا:
- في الواقع اسم الخاصية في الـ object ما زال
payload. - لكن داخل الدالة سمّيناه
amount.
أما لو حاب تغيّر شكل الـ action بالكامل (وتستخدم مثلاً action.amount بدل action.payload)، فهذا يتطلب إعداد خاص باستخدام prepare في الـ slice، وهذا نادرًا تحتاجه في البدايات، وغالبًا غير ضروري.
5️⃣ ما هو البراميتر الذي مرّرناه فعليًا؟
في الكمبوننت:
dispatch(deposit(amount));JavaScriptهنا:
amountهو البراميتر الذي مرّرناه لدالةdeposit.- غالبًا
amountجاء من state محلي:const [amount, setAmount] = useState(0); - المستخدم يكتب رقم في input → نحدث
amount→ نمرره:dispatch(deposit(amount)); // مثلاً deposit(50)
فـ Redux Toolkit تأخذ هذا الرقم وتضعه في:
action.payloadJavaScriptثم توصله للـ reducer.
خلاصة سريعة 🧠
action= object يمثل الحدث، يحتوي علىtypeوpayload.payload= البيانات اللي مررتها لما استدعيتdeposit(value).- في مثال البنك:
- من الكمبوننت:
dispatch(deposit(amount)) - ينتج:
{ type: 'bank/deposit', payload: amount } - في الـ reducer: نستخدم
action.payloadلزيادة أو إنقاص الرصيد.
- من الكمبوننت:
- تقدر تغيّر اسم براميتر الدالة (
action→eventمثلًا)،
وتقدر تعطيpayloadاسم محلي (amount) عن طريق التفكيك، لكن اسم الخاصية في الـ object يظلpayload.
لو حاب، أقدر أكتب لك console.log داخل الـ reducer يطبع لك action كاملة كأوبجكت عشان تتخيّل بالضبط كيف شكلها.