مثال 2 عن Redux Toolkit

بس بدل “عداد” نخليه رصيد بنك فيه:

  • 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
  }
}
JavaScript
3️⃣ كمبوننت بسيط: 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
}
JavaScript
  • type → اسم الحدث (إيداع، سحب، حذف، …).
  • 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;
JavaScript
4️⃣ هل يمكن تغيير اسم 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.payload
JavaScript

ثم توصله للـ reducer.

خلاصة سريعة 🧠

  • action = object يمثل الحدث، يحتوي على type و payload.
  • payload = البيانات اللي مررتها لما استدعيت deposit(value).
  • في مثال البنك:
    • من الكمبوننت: dispatch(deposit(amount))
    • ينتج: { type: 'bank/deposit', payload: amount }
    • في الـ reducer: نستخدم action.payload لزيادة أو إنقاص الرصيد.
  • تقدر تغيّر اسم براميتر الدالة (actionevent مثلًا)،
    وتقدر تعطي payload اسم محلي (amount) عن طريق التفكيك، لكن اسم الخاصية في الـ object يظل payload.

لو حاب، أقدر أكتب لك console.log داخل الـ reducer يطبع لك action كاملة كأوبجكت عشان تتخيّل بالضبط كيف شكلها.