أهلاً بيك يا صديقي المطور في العقل المدبر ومحطة التحكم المركزية لمحرر Gutenberg بالكامل: حزمة @wordpress/data.
لو أنت مبرمج واجهات وكتبت تطبيقات React متوسطة أو كبيرة، فأنت أكيد قابلت مكتبة Redux وعارف فكرتها: مخزن حالة مركزي (Single Source of Truth)، مع Actions و Reducers و Selectors. لكن لما فريق ووردبريس قرر يبني محرر جوتنبرج، اكتشفوا إن تطبيق Redux التقليدي بأسلوبه القديم مش هيكون كافي لمنظومة ضخمة ومفتوحة المصدر بيشتغل فيها مئات الإضافات والقوالب في نفس الوقت!
فريق النواة قرر يبني حزمة مخصصة اسمها @wordpress/data، وهي إعادة هندسة عبقرية لـ Redux مبنية على مفهوم سجل المخازن المعياري (Modular Store Registry). في المعمارية دي، مفيش مخزن وحيد عملاق بيبتلع كل حاجة، بل فيه “سجل مركزي” بيحتوي على عشرات المخازن المستقلة (زي مخزن المقال core/editor، مخزن البلوكات core/block-editor، مخزن الإشعارات core/notices)، مع نظام وسيط عبقري اسمه Resolvers بيجلب البيانات من الـ REST API في الخلفية تلقائياً أول ما تطلبها بدون وجع دماغ! في المقال ده، هنفكك المعمارية التحتية لـ @wordpress/data، إزاي تشترك في أحداث المحرر (زي حفظ المقال أو تغيير البلوك المحدد)، وإزاي تقفل زرار النشر برمجياً لو فيه أخطاء، مع كود إنتاجي ودراسة حالة حقيقية لشركة سياحة بالبحر الأحمر بالعامية المصرية التقنية الأصيلة.
| المعيار المعماري | معمارية @wordpress/data | مكتبة Redux التقليدية (Redux Toolkit) | سياق React (Context API) | مكتبات الحالة الخفيفة (Zustand / Jotai) |
|---|---|---|---|---|
| هيكل تخزين البيانات (Store Structure) | سجل مخازن متعدد ومقسم لنطاقات (Modular Scoped Stores: core, core/editor). | مخزن وحيد مركزي متجانس (Single Global Store) مدمج بشجرة واحدة. | موزع عبر شجرة الـ Components ويتطلب Providers متداخلة في الـ JSX. | مخازن ذرية (Atomic Stores) منفصلة تعمل خارج شجرة الـ DOM. |
| جلب البيانات غير المتزامنة (Async API Data) | مدمج تلقائياً عبر نظام الـ Resolvers الموازي دون الحاجة لـ Thunks. | يتطلب مكتبات وسيطة إضافية مثل Redux-Thunk أو Redux-Saga أو RTK Query. | يتطلب كتابة useEffect مخصص في كل مكون لجلب البيانات وتخزينها. | تتم المعالجة داخل دوال الـ Actions غير المتزامنة يدوياً. |
| طريقة الاشتراك ومراقبة التغييرات | مزدوجة: تفاعلية عبر useSelect في React، أو أمرية عبر wp.data.subscribe. | تفاعلية عبر useSelector، أو أمرية عبر store.subscribe. | تفاعلية فقط عبر useContext وتعيد رسم كل المكونات المشتركة. | تفاعلية عبر خطافات انتقائية دقيقة تمنع إعادة الرسم غير الضروري. |
| التكامل مع نظام ووردبريس ومحرر Gutenberg | أصلي ومثالي؛ كل نوافذ وأدوات المحرر تدار وتتخاطب عبر هذه الحزمة. | يتطلب بناء جسور وسيطة وضبط مخصص لربط المخزن مع لوحة تحكم ووردبريس. | محلي ولا يستطيع التحدث مع البلوكات الأخرى خارج الحاوية المشتركة. | منفصل تماماً عن أحداث النواة وزر النشر وسجل مراجعات المقال. |
| التحكم في نشر وحفظ المقال (Workflow Locks) | يوفر Actions رسمية لقفل وحظر النشر (lockPostSaving) وإظهار إشعارات النظام. | غير مدعوم أصلياً؛ يتطلب فحص يدوي والتلاعب بحالة أزرار ووردبريس. | غير مدعوم إطلاقاً. | غير مدعوم بدون بناء تكامل مخصص مع النواة. |
منظومة المخازن الرئيسية في محرر Gutenberg (Core Stores)
عشان تعرف من أين تؤكل الكتف، لازم تكون عارف أسماء المخازن الرسمية المدمجة في ووردبريس ومسؤولية كل مخزن:
-
مخزن المقال العام (core/editor):
المسؤول عن دورة حياة التدوينة المعروضة حالياً. يحتوي على معلومات: عنوان المقال، المحتوى الخام، حالة الحفظ (هل جاري الحفظ؟
isSavingPost)، هل المقال به تعديلات غير محفوظة؟ (isEditedPostDirty)، وإمكانية قفل زر النشر عبرlockPostSaving. -
مخزن محرر البلوكات (core/block-editor):
المسؤول عن شجرة البلوكات التفاعلية. يحتوي على قائمة كافة البلوكات الموجودة في المقال، من هو البلوك المحدد حالياً (
getSelectedBlock)، ويوفر دوال إدراج وحذف ونقل وتكرار البلوكات برمجياً. -
مخزن كيانات ووردبريس المركزية (core):
المسؤول عن جلب وتحديث السجلات والبيانات من قاعدة البيانات والـ REST API (مثل: المنشورات، المستخدمين، التصنيفات، التعليقات، والوسائط) عبر دوال سحرية مثل
getEntityRecord. -
مخزن التنبيهات والإشعارات (core/notices):
المسؤول عن إظهار رسائل التحذير والنجاح العائمة في أعلى المحرر أو أسفل الشاشة عبر
createErrorNoticeوcreateSuccessNotice.
سحر الـ Resolvers: كيف تجلب البيانات من السيرفر في صمت؟
في Redux العادي، لما تحب تجلب بيانات مستخدم، بتضطر تشغل Action اسمها FETCH_USER_REQUEST، وتعمل fetch، ولما يرجع تطلق FETCH_USER_SUCCESS. في @wordpress/data، أنت مش محتاج تعمل أي حاجة من دول في مكوناتك!
أنت بتطلب المعلومة مباشرة من الـ Selector:
const user = useSelect( ( select ) => {
return select( 'core' ).getEntityRecord( 'root', 'user', 1 );
}, [] );ما الذي يحدث في الخلفية برمجياً؟
- الـ Selector بيفحص الذاكرة: “هل بيانات المستخدم رقم 1 موجودة عندي في الكاش؟”.
- لو مش موجودة، الـ Selector بيطلق تلقائياً الـ Resolver المرتبط بيه.
- الـ Resolver بيروح يكلم الـ WordPress REST API (
/wp/v2/users/1)، يسحب البيانات، ويخزنها في الـ Store المركزي. - بمجرد ما البيانات توصل، الـ Selector بيعيد استدعاء نفسه، والكومبوننت بتاعك بيستلم كائن الـ
userالكامل ويبدأ يعرضه فوراً بدون كتابة سطرfetchواحد!
تطبيق عملي كامل: قفل زر نشر المقال برمجياً إذا كانت البيانات غير مكتملة
تعال نبني معمارية حقيقية لإضافة تراقب محتوى المقال؛ لو المقال يحتوي على بلوك عروض سياحية، وكاتب المحتوى نسي يحدد “تاريخ انتهاء الخصم”، نقوم فوراً بقفل زر “نشر” (Publish) في ووردبريس، ونعرض شريط تحذير أحمر في لوحة الإشعارات المركزية:
1. كود المكون الرقابي المتصل بمخزن البيانات: PublisherGuard.js
import { useEffect } from '@wordpress/element';
import { useSelect, useDispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
export default function PublisherGuard() {
// 1. استخراج دوال التحكم من المخازن المركزية
const { lockPostSaving, unlockPostSaving } = useDispatch( 'core/editor' );
const { createErrorNotice, removeNotice } = useDispatch( 'core/notices' );
// 2. قراءة بيانات المقال والبلوكات عبر Selectors دقيقة
const { isOfferIncomplete, isSaving } = useSelect( ( select ) => {
const { getBlocks } = select( 'core/block-editor' );
const { isSavingPost } = select( 'core/editor' );
const allBlocks = getBlocks();
// البحث عن بلوك العروض السياحية في شجرة المقال
const offerBlock = allBlocks.find( ( b ) => b.name === 'enterprise/travel-offer' );
// التحقق مما إذا كان البلوك موجوداً وتاريخ الانتهاء فارغ
const incomplete = offerBlock && ! offerBlock.attributes?.expiryDate;
return {
isOfferIncomplete: incomplete,
isSaving: isSavingPost(),
};
}, [] );
// 3. مراقبة الحالة واتخاذ القرار المعماري بقفل أو فتح النشر
useEffect( () => {
const lockKey = 'az-travel-offer-missing-expiry';
const noticeId = 'az-missing-expiry-notice';
if ( isOfferIncomplete ) {
// قفل زر الحفظ والنشر في لوحة تحكم ووردبريس
lockPostSaving( lockKey );
// إظهار إشعار خطأ أحمر بارز في أعلى المحرر
createErrorNotice(
__( 'تنبيه إداري: لا يمكن نشر المقال؛ برجاء تحديد تاريخ انتهاء العرض السياحي أولاً لحماية حقوق الحجز.', 'az-data' ),
{
id: noticeId,
isDismissible: false,
}
);
} else {
// فك قفل النشر فور إكمال البيانات
unlockPostSaving( lockKey );
removeNotice( noticeId );
}
// تنظيف القفل عند إلغاء تحميل المكون
return () => {
unlockPostSaving( lockKey );
removeNotice( noticeId );
};
}, [ isOfferIncomplete, lockPostSaving, unlockPostSaving, createErrorNotice, removeNotice ] );
// هذا المكون وظيفي خالص ولا يعرض أي عناصر HTML بصرية في المقال
return null;
}2. كود الاشتراك الأمري المستقل: wp.data.subscribe (Vanilla JS Listener)
إذا كنت تريد مراقبة المحرر من سكربت جافاسكريبت خارجي دون الحاجة لكتابة مكون React كامل، يمكنك استخدام دالة subscribe المباشرة:
/**
* مراقبة تغييرات حالة المقال بأمر مباشر من الكونسول أو سكربت خارجي
*/
let wasSaving = false;
const unsubscribe = wp.data.subscribe( () => {
const editorSelect = wp.data.select( 'core/editor' );
if ( ! editorSelect ) return;
const isSaving = editorSelect.isSavingPost();
const isAutosaving = editorSelect.isAutosavingPost();
// رصد لحظة انتهاء عملية الحفظ اليدوي الفعلي للمقال
if ( wasSaving && ! isSaving && ! isAutosaving ) {
console.info( '🎉 تم حفظ وتحديث المقال في قاعدة البيانات بنجاح تام!' );
const currentPost = editorSelect.getCurrentPost();
console.log( 'معرف المقال المحفوظ:', currentPost.id, 'العنوان:', currentPost.title );
}
wasSaving = isSaving;
} );
// لإلغاء الاشتراك عند عدم الحاجة:
// unsubscribe();دراسة حالة واقعية: كارثة منصة حجز رحلات شرم الشيخ والغردقة
خلفية المشروع وأبعاد الأزمة
بوابة سياحة وحجوزات فندقية كبرى في مصر تقدم عروض رحلات ومواسم سياحية في شرم الشيخ والغردقة ومرسى علم، تمتلك قائمة بريدية وصفحة فيسبوك يتابعها أكثر من 600,000 مسافر. يقوم مسؤولو التسويق بنشر عروض “خصومات الويك إند” و”باقات شهر العسل” باستخدام بلوكات ووردبريس مخصصة.
الأزمة التشخيصية (The Crisis)
المطور السابق اعتمد على تخزين صلاحية العرض كـ State محلية عادية داخل البلوك؛ ولم يربط البلوك بنظام @wordpress/data أو أحداث محرر المقال:
- أحد كتاب المحتوى المتدربين قام بنشر مقال يعرض “خصم 50% على الإقامة بفندق 5 نجوم في الجونة”، ونسي تحديد الحد الأقصى للغرف وتاريخ انتهاء العرض (ترك الحقل فارغاً).
- لأن زر “نشر” في ووردبريس لم يكن يعلم بأي أخطاء داخل البلوك، تم نشر المقال وإرساله تلقائياً في نشرة بريدية عاجلة لـ 50,000 مشترك!
- خلال ساعتين، انهال أكثر من 1,400 طلب حجز إلكتروني بالخصم غير المحدود؛ وطالب العملاء بالاستفادة من الخصم المفتوح، وتكبدت الشركة خسائر حجز فعلية تجاوزت 680,000 جنيه مصري لتفادي القضايا وشكاوى جهاز حماية المستهلك!
الحل المعماري الجذري (The Architectural Solution)
تمت إعادة بناء معمارية البلوكات التسويقية بالربط الكامل مع @wordpress/data:
- بناء مكون حراسة مركزي (PublisherGuard) يستمع لأحداث مخزن
core/block-editorومخزنcore/editor. - تفعيل دالة
lockPostSaving('missing-deal-specs')تلقائياً بمجرد إدراج البلوك؛ ولا يتم فك القفل إلا إذا أدخل الكاتب تاريخاً صالحاً في المستقبل مع عدد الغرف المتاحة. - إظهار رسالة تحذيرية حمراء غير قابلة للإغلاق عبر
core/noticesتمنع الضغط على Publish وتوجه الكاتب للحقل الناقص مباشرة. - تسجيل كافة عمليات النشر في سجل المراقبة المركزي للموقع لمنع تكرار الحادثة نهائياً.
جدول قياس مؤشرات الأداء وحماية الأرباح (Metrics Table)
| مؤشر الأداء والنزاهة التشغيلية | أثناء الأزمة (عدم الربط بـ Data Store) | بعد تطبيق معمارية @wordpress/data | نسبة التحسن والوفر المحقق |
|---|---|---|---|
| نسبة نشر مقالات بعروض ناقصة أو خاطئة | 22% من إجمالي مقالات العروض السياحية | 0.00% (استحالة النشر مع وجود قفل برمجي) | حماية مطلقة لنزاهة الصفقات والعروض |
| الخسائر المالية الناتجة عن أخطاء الحجز | 680,000 ج.م في واقعة واحدة كارثية | صفر خسائر مالية منذ تطبيق الحل | إنقاذ أرباح الشركة وميزانيتها التسويقية |
| زمن استجابة فحص الأخطاء للكاتب | لا يوجد فحص (فشل صامت) | لحظي في أجزاء من الثانية أثناء الكتابة | توجيه فوري واستباقي للمحررين |
| الامتثال لقرارات جهاز حماية المستهلك | شكاوى وإنذارات قانونية رسمية | امتثال قانوني كامل وشفافية مطلقة | حماية السجل التجاري وسمعة البراند |
الوصايا العشر لمعمارية @wordpress/data في جوتنبرج
- استخدم useSelect بحكمة وترشيد: لا تستخرج كائن المقال بالكامل إذا كنت تحتاج لعنوان المقال فقط؛ ترشيد الـ Selectors يحمي المتصفح من إعادة الرسم غير الضروري.
- اقفل زر النشر بـ lockPostSaving عند الخطر: لا تعتمد على حسن نية الكاتب؛ احمِ موقعك وأعمالك بقفل النشر برمجياً إذا كانت البيانات الإلزامية غير مكتملة.
- حدد مفتاحاً فريداً لكل قفل (Lock Key): استخدم دائماً معرفاً فريداً مثل
vendor/lock-reasonحتى لا تلغي قفلاً وضعه مكون آخر بالخطأ. - نظف القفل دائماً عند الـ Unmount: تأكد من فك القفل في دالة التنظيف المرتجعة لـ
useEffectحتى لا يظل المقال معلقاً إذا تم حذف البلوك. - اعتمد على Resolvers ولا تكتب fetch يدوياً: استخدم
getEntityRecordفي مخزنcoreودع ووردبريس يتولى التخزين المؤقت والاتصال بالشبكة. - وظف core/notices للإشعارات الرسمية: لا تبنِ أشرطة تنبيه مخصصة في الـ DOM؛ استخدم لوحة تنبيهات ووردبريس الموحدة لضمان تجربة مستخدم قياسية.
- احذر من استدعاء dispatch داخل دالة useSelect: خطاف
useSelectمخصص للقراءة فقط (Read-only)؛ أي استدعاء لتعديل الحالة بداخله سيطلق حلقة لانهائية قاتلة! - استخدم wp.data.select في الكونسول للتنقيح السريع: افتح DevTools واكتب
wp.data.select('core/editor').getCurrentPost()لاكتشاف البيانات في ثوانٍ. - افصل المخازن المخصصة لنطاقات واضحة: إذا قمت بإنشاء مخزن مخصص عبر
registerStore، سمه باسم واضح مثلmy-plugin/cart. - اختبر سلوك الحفظ التلقائي (Autosave): تأكد من أن شفرتك البرمجية تفرق بين الحفظ التلقائي في الخلفية والحفظ اليدوي الصريح بواسطة المستخدم.
معمارية بناء مخزن مخصص بالكامل عبر createReduxStore
يا صديقي المطور، عندما تبني نظاماً معقداً داخل ووردبريس—مثل نظام إدارة دورات تدريبية متقدم، أو نظام سلة مشتريات تفاعلية، أو حاسبة أسعار شحن مخصصة لشركات التوصيل في مصر—فإن الاعتماد على مخازن النواة فقط قد لا يكون كافياً. أنت بحاجة لإنشاء مخزن بيانات مخصص (Custom Redux Store) يمتلك حالة خاصة به، ويوفر Selectors و Actions يستفيد منها كل بلوك وكل نافذة في موقعك.
في الإصدارات الأولى، كنا نستخدم دالة registerStore القديمة. أما في ووردبريس الحديث، المعيار الذهبي هو استخدام دالة createReduxStore ثم تسجيلها في السجل المركزي register.
كود عملي متكامل: بناء مخزن سلة مشتريات مخصص (Enterprise Cart Store)
/**
* ملف تسجيل مخزن سلة المشتريات المركزي
* File: store/cart-store.js
*/
import { createReduxStore, register } from '@wordpress/data';
// 1. الحالة المبدئية للمخزن
const DEFAULT_STATE = {
items: [],
couponCode: '',
discountAmount: 0,
isUpdating: false,
};
// 2. معالجات الإجراءات (Actions)
const actions = {
addItem( item ) {
return {
type: 'ADD_ITEM',
payload: item,
};
},
removeItem( itemId ) {
return {
type: 'REMOVE_ITEM',
payload: itemId,
};
},
applyCoupon( code, discount ) {
return {
type: 'APPLY_COUPON',
payload: { code, discount },
};
},
setUpdating( isUpdating ) {
return {
type: 'SET_UPDATING',
payload: isUpdating,
};
},
};
// 3. الدالة الصافية لإدارة الحالة (Reducer)
function reducer( state = DEFAULT_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 ),
};
case 'APPLY_COUPON':
return {
...state,
couponCode: action.payload.code,
discountAmount: action.payload.discount,
};
case 'SET_UPDATING':
return {
...state,
isUpdating: action.payload,
};
default:
return state;
}
}
// 4. محددات القراءة (Selectors)
const selectors = {
getItems( state ) {
return state.items;
},
getItemsCount( state ) {
return state.items.length;
},
getTotalPrice( state ) {
const subtotal = state.items.reduce( ( sum, item ) => sum + ( item.price * ( item.qty || 1 ) ), 0 );
return Math.max( 0, subtotal - state.discountAmount );
},
isCartUpdating( state ) {
return state.isUpdating;
},
};
// 5. إنشاء وتسجيل المخزن في سجل ووردبريس المركزي
const STORE_NAME = 'enterprise/cart';
export const cartStore = createReduxStore( STORE_NAME, {
reducer,
actions,
selectors,
} );
// تسجيل المخزن رسمياً في سجل wp.data
register( cartStore );كيف تستهلك هذا المخزن داخل أي بلوك في موقعك؟
بمجرد تسجيل هذا المخزن، يصبح متاحاً فورياً لكافة مكونات React بنفس أسلوب مخازن النواة تماماً:
import { useSelect, useDispatch } from '@wordpress/data';
import { Button } from '@wordpress/components';
import { cartStore } from './store/cart-store';
export function CartSummaryWidget() {
// قراءة البيانات من المخزن المخصص
const { total, count } = useSelect( ( select ) => {
const store = select( cartStore ); // أو select('enterprise/cart')
return {
total: store.getTotalPrice(),
count: store.getItemsCount(),
};
}, [] );
// سحب دوال التحديث
const { addItem } = useDispatch( cartStore );
return (
عدد العناصر: { count }
الإجمالي: { total.toLocaleString() } ج.م
);
}معمارية التحديثات التفاؤلية (Optimistic Updates in Data Stores)
في تجربة المستخدم الحديثة، عندما يضغط المستخدم على زر “حفظ” أو “إعجاب”، الانتظار حتى يرد السيرفر بعد 500 مللي ثانية يشعر الزائر بالبطء والتردد.
تعتمد ووردبريس في مخزن core على ما يسمى بـ Optimistic Updates:
- يقوم المخزن فورياً بتحديث الواجهة في الذاكرة الحية وإظهار النتيجة للمستخدم فور النقر (وكأن السيرفر وافق بالفعل).
- في الخلفية، يقوم الـ Control بإرسال طلب الـ HTTP الفعلي لنقطة النهاية في الـ REST API.
- إذا نجح الطلب، يتم تأكيد الحالة بهدوء دون أي وميض.
- إذا فشل الطلب (مثل انقطاع الإنترنت أو حدوث خطأ 500)، يقوم المخزن تلقائياً بـ Rollback والتراجع عن التغيير، مع إطلاق إشعار خطأ في
core/noticesلإعلام المستخدم بسبب الفشل.
هذا المستوى الرفيع من النضج الهندسي هو ما يجعل تطبيقات ووردبريس ومحرر Gutenberg تنافس أرقى تطبيقات الـ Single Page Applications العالمية في السرعة وسلاسة الاستجابة.
معايير توثيق واختبار مخازن البيانات في بيئة الإنتاج
عندما تطور مخازن بيانات مركزية للمؤسسات الكبرى، احرص دائماً على كتابة اختبارات وحدة آلية (Unit Tests) للـ Reducers والـ Selectors بشكل منفصل عن مكونات الواجهة. بما أن الـ Reducer دالة صافية خالية من أي آثار جانبية، يمكنك اختبار كافة الحالات الممكنة لسلة المشتريات أو إدارة الحجوزات بدقة حاسوبية 100%، مما يضمن أمان المعاملات المالية واستقرار موقعك في كافة الظروف.
الأسئلة الشائعة حول معمارية @wordpress/data (20 سؤالاً وجواباً تفصيلياً)
1. ما هي حزمة @wordpress/data في ووردبريس؟
هي وحدة إدارة الحالة الرسمية في Gutenberg، مبنية على أفكار Redux ومصممة لتوفير سجل مخازن معياري يدير كافة بيانات المحرر والموقع بسلاسة وتناغم.
2. كيف أقرأ بيانات من مخزن معين باستخدام React Hook؟
باستخدام خطاف useSelect، مثل: const title = useSelect( (select) => select('core/editor').getEditedPostAttribute('title') );.
3. كيف أطلق إجراء (Action) لتعديل الحالة في مخزن معين؟
باستخدام خطاف useDispatch، مثل: const { editPost } = useDispatch('core/editor'); editPost({ title: 'عنوان جديد' });.
4. ما هو الـ Resolver في @wordpress/data؟
هو وظيفة وسيطة غير متزامنة مرتبطة بـ Selector معين؛ عندما يُطلب Selector وتكون بياناته غير متوفرة، يقوم الـ Resolver بجلبها تلقائياً من الـ REST API وتحديث المخزن في صمت.
5. كيف أقفل زر نشر المقال في ووردبريس برمجياً؟
باستدعاء lockPostSaving( 'unique-lock-key' ) من مخزن core/editor، ولن يتمكن الكاتب من النشر حتى تستدعي unlockPostSaving بنفس المفتاح.
6. ما هي وظيفة دالة wp.data.subscribe؟
تتيح الاستماع المباشر لأي تغيير يطرأ على شجرة البيانات المركزية في ووردبريس من كود جافاسكريبت نقي خارج نطاق مكونات React.
7. كيف أعرف ما إذا كان المقال جاري حفظه حالياً في الخلفية؟
باستخدام المحدد select('core/editor').isSavingPost() أو isAutosavingPost() للتحقق من الحفظ التلقائي.
8. ما هو الفرق بين مخزن core/editor ومخزن core/block-editor؟
مخزن core/editor يدير المقال ككل (الحفظ، النشر، العنوان، المسودة)، بينما core/block-editor يدير شجرة البلوكات الفردية وترتيبها وتحديدها.
9. كيف أظهر رسالة خطأ حمراء للمحرر باستخدام core/notices؟
باستدعاء createErrorNotice( 'نص الخطأ', { id: 'my-notice-id' } ) من إجراءات مخزن core/notices.
10. هل يمكن إنشاء مخزن مخصص جديد خاص بإضافتي البرمجية؟
نعم؛ باستخدام دالة register( createReduxStore( 'my-plugin/store', { reducer, actions, selectors } ) ) وتسمية المخزن باسم فريد.
11. لماذا يحظر استدعاء dispatch داخل جسم دالة useSelect؟
لأن useSelect مخصصة للقراءة الصافية؛ وتعديل الحالة بداخلها يطلق إعادة رسم متكررة تدخل التطبيق في حلقة لانهائية (Infinite Loop) تجمد المتصفح.
12. كيف أحصل على قائمة كافة البلوكات الموجودة في المقال الحالي؟
باستخدام المحدد select('core/block-editor').getBlocks() الذي يعيد مصفوفة كائنات البلوكات الكاملة.
13. كيف أكتشف البلوك المحدد حالياً بواسطة الفأرة في المحرر؟
باستخدام المحدد select('core/block-editor').getSelectedBlock().
14. ما الفارق بين getPostAttribute و getEditedPostAttribute؟
الأولى تقرأ القيمة الأصلية للمقال المحفوظة في قاعدة البيانات، بينما الثانية تقرأ القيمة المعدلة حالياً في الذاكرة الحية قبل الحفظ.
15. كيف أختبر أداء استعلامات useSelect في مشروعي؟
بمراقبة عدد مرات استدعاء المحدد، والحرص على إرجاع قيم أولية (Primitives) أو مذكرنة (Memoized) لتجنب إعادة رسم المكون دون تغيير حقيقي.
16. هل تعمل حزمة @wordpress/data في الواجهة الأمامية للموقع (Frontend)؟
يمكن تضمينها وتشغيلها في الواجهة الأمامية إذا كنت تبني تطبيقات تفاعلية أحادية الصفحة (SPA)، ولكنها محملة تلقائياً في شاشة التحرير بالخلفية.
17. كيف أتحقق مما إذا كان المحرر قد أجرى تعديلات غير محفوظة (Dirty Post)؟
باستخدام المحدد select('core/editor').isEditedPostDirty().
18. هل تدعم حزمة @wordpress/data أدوات الفحص Redux DevTools؟
نعم؛ يمكنك ربطها بـ Redux DevTools في متصفح كروم لمشاهدة سجل الـ Actions وتغيرات الحالة عبر الزمن (Time Travel Debugging).
19. كيف أحذف إشعاراً مسجلاً في core/notices برمجياً؟
باستدعاء دالة removeNotice( 'notice-id' ) وتمرير نفس المعرف الذي تم إنشاؤه به.
20. ما هي القاعدة الذهبية لإتقان معمارية @wordpress/data للمطورين المحترفين؟
“لا تبحث في الشاشة بالـ DOM، بل اسأل الـ Store في الذاكرة؛ كل حركة في ووردبريس هي Action، وكل معلومة مسجلة في Selector جاهز لخدمتك”.
المراجع الرسمية والتوثيق المعتمد
- WordPress Data Package Official Documentation (@wordpress/data)
- WordPress Core Editor Data Store API Reference (core/editor)
- WordPress Block Editor Data Store Reference (core/block-editor)
- WordPress Core Notices Data Store Reference (core/notices)
- Redux Core Architecture: Motivation and Predictable State Container Design


اترك تعليقاً
يجب أنت تكون مسجل الدخول لتضيف تعليقاً.