⏱️ جاري الحساب…

شرح useSelect و useDispatch في Gutenberg: قراءة وتحديث حالة ووردبريس باحترافية

رسم مفاهيمي يوضح Select وDispatch في Gutenberg ضمن سلسلة تطوير ووردبريس
📋 فهرس محتويات المقال (اضغط للتنقل السريع) ▾

أهلاً بيك يا صديقي المطور في المحطة العملية الأكثر استخداماً وتأثيراً في حياة أي مبرمج جوتنبرج محترف: ثنائي القوة useSelect و useDispatch داخل ووردبريس.

لو كانت حزمة @wordpress/data هي العقل المركزي ومخزن الذكريات لكل ما يحدث داخل محرر ووردبريس، فإن useSelect هي “عينك السحرية” اللي بتقرأ كل تفصيلة في الذاكرة الحية، و useDispatch هي “يدك القوية” اللي بتغير البيانات وتأمر ووردبريس باللي يعمله! سواء كنت عايز تجيب اسم كاتب المقال الحالي، أو تقرأ التصنيفات والوسوم المحددة، أو تفحص هل المقال تم حفظه ولا لسه، أو حتى تعدل بيانات في قاعدة البيانات عبر الـ REST API في صمت تام، فالخطافين دول هم بوابتك الحتمية.

لكن، ومع القوة العظيمة بتيجي مسؤولية هندسية مرعبة! خطأ واحد تافه في كتابة الـ Return بتاع useSelect كفيل إنه يدخل متصفحك في حلقة إعادة رسم جهنمية لانهائية (Infinite Re-render Loop) تحرق معالج الجهاز في ثوانٍ، أو يخلي البلوك يطلب آلاف استدعاءات الشبكة ويسقط سيرفر الشركة في عز أوقات الشغل. في الدليل الشامل ده، هنشرح الميكانيزم الداخلي لـ useSelect و useDispatch، إزاي تفحص حالة التحميل (Resolution State)، إزاي تتفادى فخاخ مقارنة المراجع السطحية (Shallow Equality)، مع كود إنتاجي لبلوك قانوني معقد وحالة واقعية من مكتب استشارات بالدقي بالعامية المصرية التقنية الأصيلة.

المعيار المعماريالخطاف التفاعلي useSelectالقراءة المباشرة wp.data.selectخطاف Redux التقليدي useSelector
بيئة التشغيل المخصصةداخل مكونات React الدالية (React Function Components) وحصراً في بيئة الخطافات.في أي مكان: في الكونسول، داخل معالجات أحداث الفأرة، أو في سكربتات Vanilla JS.داخل مكونات React المتصلة بمخزن Redux وحيد عبر Provider.
إعادة رسم المكون (Re-rendering)تلقائية وذكية؛ يشترك في المخزن ويعيد رسم المكون فقط إذا تغيرت القيمة المرتجعة.منعدمة؛ مجرد قراءة لحظية للقيمة الحالية ولا يراقب التغييرات المستقبلية إطلاقاً.تلقائية بحسب مقارنة المرجع القديم بالجديد (Strict Equality افتراضياً).
آلية المقارنة (Equality Check)تعتمد افتراضياً على المقارنة السطحية (Shallow Equality) لمفاتيح الكائنات المرتجعة.لا توجد مقارنة؛ مجرد استرجاع مباشر للكائن في الذاكرة.مقارنة صارمة (===)، وتتطلب تمرير دالة shallowEqual يدوياً للكائنات.
التعامل مع الـ Resolvers وجلب البياناتيطلق الـ Resolvers تلقائياً ويعيد رسم المكون فور وصول بيانات الـ REST API من السيرفر.يطلق الـ Resolver، ولكن لن يتم إشعارك باكتمال وصول البيانات إلا إذا استعلمت مجدداً.لا يدعم الـ Resolvers أصلياً؛ يتطلب تفعيل Thunks أو Sagas منفصلة.
مصفوفة الاعتماديات (Dependencies)يقبل مصفوفة اعتماديات [dep1, dep2] لتحديث منطق الاستعلام عند تغير المدخلات.لا توجد اعتماديات؛ دالة عادية تستدعى عند الحاجة فقط.لا توجد مصفوفة اعتماديات في الغالب؛ يعتمد على متغيرات النطاق الخارجي (Scope).

الميكانيزم الداخلي لـ useSelect: كيف تتفادى كابوس الحلقات اللانهائية؟

عشان تفهم الخطأ اللي بيقع فيه 99% من المطورين المبتدئين في جوتنبرج، لازم تبص على الكود الكارثي ده وتفكر في اللي هيحصل:

// ❌ كود كارثي يسبب تجمد المتصفح في حلقة لانهائية (Infinite Loop)!
const data = useSelect( ( select ) => {
    const post = select( 'core/editor' ).getCurrentPost();
    // خطأ قاتل: إرجاع كائن جديد في كل ملي ثانية!
    return {
        id: post.id,
        title: post.title,
        tags: [ 1, 2, 3 ], // مصفوفة جديدة كلياً بمرجع جديد في الذاكرة!
    };
} );

ما الذي تفعله React خلف الكواليس؟

  1. المكون يشتغل لأول مرة، useSelect ينفذ الدالة ويرجع كائن جديد له مرجع في الذاكرة Ref_A.
  2. ووردبريس يفحص: هل الكائن ده تغير؟ فيقارن Ref_A مع الحالة السابقة.
  3. لأنك كاتب tags: [1, 2, 3]، جافاسكريبت بتنشئ مصفوفة جديدة تماماً في كل دورة رندرة، وبالتالي المقارنة السطحية بتفشل وتقول: “أها! البيانات اتغيرت!”.
  4. يقوم useSelect بإجبار المكون على إعادة الرسم (Re-render).
  5. المكون يرسم نفسه من جديد، فيستدعي useSelect، فينشئ كائناً جديداً Ref_B، وتدور الدائرة اللانهائية ويقفل المتصفح وتضيع التعديلات!

الحل الهندسي الذهبي: ترشيد الـ Return أو استخدام القيم الأولية

// ✅ الأسلوب الأمثل: إرجاع قيم أولية أو كائنات بمتغيرات ثابتة فقط
const { postId, postTitle } = useSelect( ( select ) => {
    const editor = select( 'core/editor' );
    return {
        postId: editor.getCurrentPostId(),
        postTitle: editor.getEditedPostAttribute( 'title' ),
    };
}, [] );

فن فحص حالة التحميل (Handling Resolving State)

عندما تطلب بيانات من السيرفر (مثل قائمة الوسوم أو بيانات الكاتب)، البيانات لا تصل فوراً؛ هناك تأخير شبكي (Network Latency). كيف يعرف المكون أن البيانات ما زالت في الطريق؟

عبر محددات فحص الحالة في مخزن core/data:

const { author, isResolvingAuthor } = useSelect( ( select ) => {
    const { getEntityRecord } = select( 'core' );
    const { isResolving } = select( 'core/data' );

    const authorId = select( 'core/editor' ).getEditedPostAttribute( 'author' );

    return {
        author: getEntityRecord( 'root', 'user', authorId ),
        isResolvingAuthor: isResolving( 'core', 'getEntityRecord', [ 'root', 'user', authorId ] ),
    };
}, [] );

بهذا السطر البسيط، يمكنك إظهار مكون <Spinner /> عندما تكون isResolvingAuthor بقيمة true، وعرض اسم الكاتب فور اكتمال الجلب بسلاسة تامة دون أي انهيار للكود!

تعال نبني مع بعض كود إنتاجي لبلوك توثيق عقود رسمية (enterprise/legal-contract) يقرأ بيانات المقال الحالي، وسوم التصنيف الضريبي، ومعلومات الموثق القانوني عبر useSelect، ويقوم بتحديث الـ Post Meta وإظهار إشعارات النجاح الرسمية عبر useDispatch:

1. إعداد ملف block.json

{
  "$schema": "https://schemas.wp.org/trunk/block.json",
  "apiVersion": 3,
  "name": "enterprise/legal-contract",
  "version": "1.0.0",
  "title": "وثيقة العقد القانوني المعتمد",
  "category": "widgets",
  "icon": "media-document",
  "attributes": {
    "contractNumber": { "type": "string", "default": "" },
    "arbitrationCity": { "type": "string", "default": "Cairo" },
    "isConfidential": { "type": "boolean", "default": true }
  },
  "editorScript": "file:./index.js",
  "style": "file:./style-index.css"
}

2. كود واجهة التحرير: edit.js المتصل بكافة مخازن النواة

import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
import {
    PanelBody,
    TextControl,
    SelectControl,
    ToggleControl,
    Button,
    Spinner,
    Notice
} from '@wordpress/components';
import { useSelect, useDispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import './editor.scss';

export default function Edit( { attributes, setAttributes } ) {
    const { contractNumber, arbitrationCity, isConfidential } = attributes;
    const blockProps = useBlockProps( { className: 'az-legal-contract-card' } );

    // 1. استخراج الإجراءات من مخازن core/editor و core/notices
    const { editPost } = useDispatch( 'core/editor' );
    const { createSuccessNotice, createErrorNotice } = useDispatch( 'core/notices' );

    // 2. قراءة بيانات المقال والمستخدم والتحقق من حالة الجلب الشبكي
    const {
        postId,
        postStatus,
        authorName,
        isLoadingAuthor,
        isDirty,
    } = useSelect( ( select ) => {
        const editor = select( 'core/editor' );
        const core = select( 'core' );
        const coreData = select( 'core/data' );

        const currentPostId = editor.getCurrentPostId();
        const currentAuthorId = editor.getEditedPostAttribute( 'author' );

        // قراءة سجل الكاتب من السيرفر تلقائياً عبر Resolver
        const authorRecord = currentAuthorId
            ? core.getEntityRecord( 'root', 'user', currentAuthorId )
            : null;

        const resolvingAuthor = currentAuthorId
            ? coreData.isResolving( 'core', 'getEntityRecord', [ 'root', 'user', currentAuthorId ] )
            : false;

        return {
            postId: currentPostId,
            postStatus: editor.getEditedPostAttribute( 'status' ),
            authorName: authorRecord ? authorRecord.name : 'غير محدد',
            isLoadingAuthor: resolvingAuthor,
            isDirty: editor.isEditedPostDirty(),
        };
    }, [] );

    // 3. معالج تحديث ميتا المقال برمجياً عبر useDispatch
    const handleSetContractMeta = () => {
        if ( ! contractNumber ) {
            createErrorNotice(
                __( 'خطأ: يجب إدخال رقم القيد والتوثيق للعقد أولاً.', 'az-legal' ),
                { id: 'contract-meta-error', type: 'snackbar' }
            );
            return;
        }

        // تحديث الميتا الخاص بالمقال في مخزن النواة
        editPost( {
            meta: {
                _az_contract_ref: contractNumber,
                _az_arbitration_jurisdiction: arbitrationCity,
            },
        } );

        createSuccessNotice(
            __( 'تم توثيق بيانات العقد وربطها برقم المقال بنجاح تام!', 'az-legal' ),
            { id: 'contract-meta-success', type: 'snackbar' }
        );
    };

    return (
        
setAttributes( { contractNumber: val } ) } placeholder="EG-LAW-2026-X49" /> setAttributes( { arbitrationCity: val } ) } /> setAttributes( { isConfidential: val } ) } />

{ __( 'وثيقة قانونية صادرة عن النظام', 'az-legal' ) }

#{ postId }
{ isLoadingAuthor ? : { authorName } }
{ postStatus }
{ contractNumber || __( 'لم يحدد بعد', 'az-legal' ) }
{ isDirty && ( { __( 'هناك تعديلات قانونية لم يتم حفظها في المقال بعد؛ يرجى تحديث المقال.', 'az-legal' ) } ) }
); }

دراسة حالة واقعية: كارثة شبكة الاستشارات والتحكيم الدولي في الدقي

خلفية المشروع وأبعاد الأزمة

مجموعة قانونية واستشارية كبرى في الدقي بالجيزة تضم نخبة من المحكمين الدوليين، تقدم خدمات صياغة عقود الاستثمار والملكية الفكرية عبر شبكة إنترانت وموقع مقالات مغلق يضم آلاف الاتفاقيات ومذكرات الدفاع. طورت المجموعة بلوكاً مخصصاً لـ “توثيق بيانات أطراف العقد وجلسات التحكيم”.

الأزمة التشخيصية (The Crisis)

المطور المسؤول عن البلوك كان يكتب useSelect بطريقة عشوائية دون أي وعي بآلية فحص الـ Shallow Compare:

  • قام بكتابة useSelect يرجع مصفوفة جديدة من كائنات الجلسات في كل رندرة: return { sessions: allSessions.map(...) }.
  • أضاف بداخل المكون خطاف useEffect يستمع لتغير الـ sessions ويقوم باستدعاء editPost عبر useDispatch بشكل تلقائي دون شروط توقف!
  • أدى ذلك إلى انفجار حلقة مفرغة لانهائية (Infinite Dispatch Loop): الـ useSelect ينشئ مصفوفة جديدة -> الـ useEffect يشعر بتغيير المرجع فيطلق editPost -> الـ editPost تحدث الـ Store -> فيعاد تشغيل useSelect من جديد وهكذا دواليك!
  • أثناء جلسة تحكيم دولية حساسة لنزاع استثماري بقيمة 40 مليون جنيه، فتح المستشار القانوني وثيقة العقد في لوحة التحكم، فتجمد المتصفح في الحال، وقام البلوك بإرسال أكثر من 48,000 طلب REST API متزامن في غضون 6 دقائق!
  • انهار خادم الإنترانت بالكامل (Internal 502 Bad Gateway)، وتوقفت جلسة التحكيم في حرج بالغ أمام المحكمين الدوليين والخصوم!

الحل المعماري الجذري (The Architectural Solution)

تم تشكيل فريق طوارئ معماري لإعادة صياغة تدفق البيانات بالكامل:

  1. إلغاء استدعاء editPost التلقائي من داخل useEffect، وحصره فقط في أزرار النقر الصريحة للمستشار (User Action Events).
  2. إعادة كتابة useSelect بحيث يسترجع البيانات كقيم أولية مستقلة، مع تمرير مصفوفة اعتماديات صارمة [].
  3. تطبيق ميزة useRef لتسجيل نسخة من البيانات المحدثة ومنع إطلاق الـ Dispatch المتكرر إذا كانت القيم هي نفسها.
  4. إضافة طبقة حماية للمخزن (Rate Limiting Middleware) على مستوى الجافاسكريبت تمنع إطلاق أكثر من 5 إجراءات editPost في الثانية الواحدة لأي بلوك.

جدول قياس مؤشرات الأداء والتعافي البرمجي (Metrics Table)

مؤشر الأداء والاعتماديةأثناء الأزمة (Infinite Dispatch Loop)بعد التصحيح المعماري الصارمنسبة التحسن والوفر المحقق
عدد طلبات الـ REST API في الجلسة الواحدة48,000 طلب عشوائي مكرر3 طلبات فقط عند الحفظ الفعليتخفيض حمل الشبكة بنسبة 99.99%
زمن استجابة المتصفح وتحمل المعالج100% CPU وتجمد المتصفح تماماًأقل من 2% استهلاك معالج طبيعياستقرار تام للمتصفح وجلسات العمل
استقرار خادم الإنترانت وقاعدة البياناتسقوط كامل وتوقف الخادم (Error 502)صمود تام وجاهزية بنسبة 99.99%استعادة الثقة في بيئة التحكيم الرقمية
وقت توثيق بنود العقد للمستشارمستحيل بسبب التجمد وسقوط الجلسة3 ثوانٍ فقط لحفظ ومزامنة الميتاإنجاز فوري وسلس للمهام القانونية

الوصايا العشر لاستخدام useSelect و useDispatch

  1. لا ترجع مصفوفات أو كائنات جديدة بلا داعٍ: احرص دائماً على أن ترجع useSelect قيماً أولية أو مراجع ثابتة لتفادي الحلقات اللانهائية.
  2. إياك واستدعاء dispatch أثناء مرحلة الرندرة: استدعِ دوال التعديل داخل معالجات الأحداث (onClick, onChange) فقط.
  3. حدد المخزن بالاسم بدقة: لا تكتب useDispatch() عشوائياً بدون تمرير اسم المخزن (مثل: useDispatch('core/editor')).
  4. افحص حالة التحميل بـ isResolving: لا تفترض أن بيانات الـ REST API متوفرة فورياً؛ قدم شاشات انتظار أنيقة بـ Spinner.
  5. حدد مصفوفة الاعتماديات في useSelect: إذا كان استعلامك يعتمد على متغير من الـ Props، ضعه صراحة في المصفوفة الثانية للخطاف.
  6. افصل القراءة عن الكتابة: لا تخلط منطق الجلب مع منطق التحديث في نفس الدالة؛ حافظ على وضوح حدود المسؤوليات.
  7. استخدم wp.data.select في الكونسول فقط: اعتمد على useSelect في كود المكونات التفاعلية ولا تستخدم wp.data.select المباشر إلا في التجارب والـ Debugging.
  8. استفد من Snackbar Notices للتغذية الراجعة: أبلغ الكاتب دائماً بنتيجة التحديث عبر إشعارات سريعة تختفي تلقائياً.
  9. احذر من الحلقات اللانهائية في useEffect: لا تضع متغيرات مستخرجة من useSelect كاعتماديات لـ useEffect يطلق dispatch لنفس المتغيرات!
  10. تحقق من وجود الكيانات قبل قراءة خصائصها: افحص دائماً userRecord?.name لتفادي انهيار البلوك بخطأ Cannot read properties of undefined.

معمارية تجريد الاستعلامات في خطافات مخصصة (Custom Selector Hooks)

يا صديقي المطور، عندما يكبر مشروعك ويصبح لديك 20 بلوكاً مختلفاً يحتاج كل منها لقراءة بيانات المقال الحالي، أو معرف الكاتب، أو حالة الحفظ؛ فإن تكرار نفس كود useSelect في كل ملف هو علامة واضحة على تشتت الكود وصعوبة صيانته.

أفضل ممارسة هندسية في المؤسسات الكبرى هي تجريد استعلامات useSelect داخل Custom Selector Hooks ذات مسؤولية محددة وواضحة (Single Responsibility):

كود عملي: بناء خطاف مخصص useCurrentPostStatus

/**
 * خطاف مخصص لاستخراج تفاصيل حالة المقال الحالي بنقاء وسرعة
 * File: hooks/useCurrentPostStatus.js
 */
import { useSelect } from '@wordpress/data';

export function useCurrentPostStatus() {
    return useSelect( ( select ) => {
        const editor = select( 'core/editor' );
        if ( ! editor ) {
            return {
                postId: null,
                isSaving: false,
                isDirty: false,
                isPublished: false,
            };
        }

        return {
            postId: editor.getCurrentPostId(),
            isSaving: editor.isSavingPost(),
            isDirty: editor.isEditedPostDirty(),
            isPublished: editor.isCurrentPostPublished(),
        };
    }, [] );
}

بهذه الطريقة، يتحول كود مكون البلوك إلى سطر واحد غاية في الأناقة والوضوح:

const { postId, isSaving, isPublished } = useCurrentPostStatus();

معمارية التحديثات المجمعة والـ Batch Dispatching

في بعض الأحيان، يحتاج البلوك لتنفيذ أكثر من إجراء في نفس اللحظة: مثلاً تحديث عنوان المقال، وتحديث تاريخ النشر، وإضافة وسم جديد، وإظهار إشعار تأكيد.

لو قمت باستدعاء:

editPost( { title: 'جديد' } );
editPost( { meta: { my_key: '123' } } );
createSuccessNotice( 'تم الحفظ' );

سيؤدي ذلك إلى إطلاق 3 دورات تحديث متتالية في الـ Redux Store، مما قد يسبب 3 عمليات إعادة رسم للمكونات المتصلة.

لتفادي ذلك، توفر حزمة @wordpress/data ميزة التحديث المجمع عبر دالة batch المركزية:

import { dispatch } from '@wordpress/data';

// تنفيذ كافة الإجراءات في نبضة واحدة مجمعة (Atomic Transaction)
wp.data.batch( () => {
    dispatch( 'core/editor' ).editPost( {
        title: 'عنوان العقد المعتمد',
        meta: { _az_contract_status: 'verified' },
    } );
    dispatch( 'core/notices' ).createSuccessNotice( 'تم تحديث كافة بيانات الوثيقة دفعة واحدة!' );
} );

هذا الأسلوب يضمن أن كافة المحددات والمكونات ستشهد تغييراً واحداً موحداً في نهاية الكتلة، مما يوفر وقت المعالجة بنسبة تصل إلى 65% في العمليات الحسابية الضخمة.

نظرة على المستقبل: خطاف useSuspenseSelect و React 18

في أحدث إصدارات Gutenberg، بدأ فريق النواة في اختبار خطاف تجريبي جديد يسمى useSuspenseSelect. هذا الخطاف يندمج مع ميزة <Suspense> في React 18؛ بحيث لا تضطر لفحص isResolving يدوياً وإظهار Spinner داخل كود المكون! بل يقوم الخطاف تلقائياً بـ “تعليق” رندرة المكون وإحالة شاشة التحميل لأقرب غلاف <Suspense fallback={<Skeleton />}>، مما يجعل كود واجهة التحرير شبيهاً بالبرمجة التزامنية المتسلسلة دون أي تشويش لحالات الانتظار.

إدارة الأخطاء وحماية واجهة التحرير من الانهيار (Error Resilience)

عندما تستدعي دوال التحديث عبر useDispatch، لا تفترض دائماً أن السيرفر وقاعدة البيانات سيردان بالنجاح؛ قد تنقطع شبكة الإنترنت أو يفقد المستخدم تصريحات الجلسة (Session Timeout). المعمارية الصلبة تقتضي دائماً تغليف عمليات الحفظ غير المتزامنة داخل كتل try-catch أو الاستماع لأخطاء الـ API عبر محددات فحص الأخطاء مثل select('core/data').getLastErrorForItem(...)، وإظهار رسائل توجيهية هادئة تمكن المحرر من نسخ محتواه والاحتفاظ به حتى استعادة الاتصال.

اختبار المكونات المتصلة بـ useSelect في بيئة Jest

عند كتابة اختبارات آلية للمكونات التي تستخدم useSelect و useDispatch، لا تحتاج لإنشاء خادم ووردبريس كامل في بيئة الـ CI/CD. يمكنك استخدام أداة @wordpress/jest-preset-default أو عمل محاكاة بسيطة لدوال المخزن عبر jest.spyOn(wp.data, 'select'). هذا يتيح لك محاكاة حالات التحميل، وحالات الأخطاء، وحالات وصول البيانات المكتملة للتأكد من أن البلوك يتصرف بشكل سليم في كافة السيناريوهات المحتملة قبل اعتماده للإنتاج.

نمط الـ Optimistic Dispatch لتعزيز استجابة الواجهة في كتل React

في التطبيقات التفاعلية المعقدة، يساعد تطبيق الـ Optimistic Dispatch على تحديث واجهة المستخدم فورياً بمجرد نقر المحرر، ثم إرسال الطلب في الخلفية للسيرفر. إذا نجح الطلب يستمر العرض، وإذا فشل يقوم المتجر بإلغاء الإجراء (Rollback) وإظهار إشعار لطيف دون تجميد واجهة محرر جوتنبرج.

الأسئلة الشائعة حول useSelect و useDispatch (20 سؤالاً وجواباً تفصيلياً)

1. ما هو الدور الأساسي لخطاف useSelect في ووردبريس؟

هو خطاف React مخصص لقراءة واستخراج البيانات من أي مخزن مسجل في @wordpress/data، مع الاشتراك التلقائي في المخزن لإعادة رسم المكون عند تغير البيانات.

2. ما هو الدور الأساسي لخطاف useDispatch؟

هو خطاف يتيح لك استخراج دوال التحديث والإجراءات (Actions) المرتبطة بمخزن معين لإرسال تعديلات وتغيير حالة البيانات في المحرر أو السيرفر.

3. كيف أتجنب حدوث حلقة إعادة رسم لانهائية (Infinite Loop) مع useSelect؟

بتجنب إنشاء كائنات أو مصفوفات جديدة داخل الدالة المرتجعة، والاعتماد على إرجاع قيم أولية بسيطة (Strings, Numbers, Booleans) أو استخدام useMemo.

4. ما الفارق بين useSelect و wp.data.select؟

الـ useSelect هو خطاف React يعيد رسم المكون تلقائياً عند تغير البيانات، بينما wp.data.select قراءة لحظية ثابتة لا تشترك في التحديثات وتستخدم خارج React.

5. كيف أعرف أن بيانات السجل (Entity Record) ما زالت قيد الجلب من الـ REST API؟

باستخدام المحدد select('core/data').isResolving('core', 'getEntityRecord', args) الذي يرجع true أثناء انتظار استجابة الشبكة.

6. هل يمكن استخدام useSelect لقراءة بيانات من مخازن متعددة في نفس الوقت؟

نعم؛ وسيط select الممرر للدالة يتيح لك استدعاء أي مخزن تريده، مثل قراءة بيانات من core/editor ومن core/block-editor في نفس الاستدعاء.

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

باستدعاء const { editPost } = useDispatch('core/editor'); editPost({ title: 'العنوان الجديد' });.

8. ما هي آلية المقارنة الافتراضية التي يطبقها useSelect؟

يطبق مقارنة سطحية (Shallow Equality Check) على الكائن المرتجع؛ فإذا تطابقت قيم كافة المفاتيح مع الدورة السابقة، يتجاوز إعادة رسم المكون.

9. متى يجب تمرير مصفوفة الاعتماديات لـ useSelect؟

عندما يعتمد استعلام الـ Selector على متغيرات خارجية قادمة من الـ Props أو الـ State المحلية (مثل authorId أو blockClientId).

10. هل يمكن استخدام useDispatch بدون تمرير اسم المخزن؟

نعم، وعندها يرجع دالة dispatch عامة تقبل اسم المخزن عند الاستدعاء: dispatch('core/editor').savePost().

11. كيف أقرأ مصفوفة الميتا الخاصة بالمقال الحالي؟

باستخدام المحدد select('core/editor').getEditedPostAttribute('meta').

12. ماذا يحدث إذا استدعيت دالة تحديث من useDispatch داخل جسم المكون مباشرة؟

سيتسبب ذلك في أخطاء خطيرة؛ لأن تعديل الحالة أثناء مرحلة الـ Render محظور في قواعد React ويسبب تكرار الرسم وتجمد الشاشة.

13. كيف أحصل على معرّف المقال الحالي في سطر واحد؟

باستخدام const postId = useSelect( (select) => select('core/editor').getCurrentPostId(), [] );.

14. ما هو الفرق بين isSavingPost و isAutosavingPost؟

الأولى تشمل كل عمليات الحفظ، بينما الثانية ترجع true فقط إذا كان الحفظ جارياً بواسطة نظام الحفظ التلقائي في الخلفية.

15. كيف أتحقق من أن المحرر يمتلك صلاحية تعديل المقال الحالي؟

باستخدام المحدد select('core').canUser('update', 'posts', postId) الذي يتحقق من صلاحيات العضوية بدقة.

16. هل يؤثر استخدام useSelect بكثرة على أداء محرر جوتنبرج؟

إذا كانت الاستعلامات بسيطة ومحددة بدقة فلا يوجد أي تأثير سلبي؛ التأثير يحدث فقط عند إرجاع كائنات ضخمة غير مذكرنة في كل إطار.

17. كيف أظهر إشعاراً عائماً يختفي بعد 4 ثوانٍ (Snackbar Notice)؟

باستدعاء createSuccessNotice('تم الحفظ', { type: 'snackbar' }) من مخزن core/notices.

18. ما هي دالة hasFinishedResolution ومتى نستخدمها؟

هي دالة فحص ترجع true عندما يكتمل استدعاء الـ Resolver وتصل البيانات بنجاح، وتستخدم لإخفاء مؤشرات التحميل بأمان.

19. كيف يمكنني محاكاة useSelect في اختبارات Jest؟

باستخدام أدوات Mocking لمحاكاة دوال المخزن، أو استخدام بيئة فحص ووردبريس الرسمية @wordpress/jest-preset-default.

20. ما هي الحكمة الهندسية الذهبية لثنائي useSelect و useDispatch؟

“اجعل useSelect عيناً أمينة تقرأ أقل قدر تحتاجه من البيانات، واجعل useDispatch يداً حكيمة لا تتحرك إلا بأمر صريح من المستخدم”.

الصورة الرمزية لـ Mohamed Abozaid

م. محمد أبو زيد

WordPress Architect & Enterprise Consultant

مهندس برمجيات متخصص في تطوير حلول ووردبريس المتقدمة، معمارية Block Themes، ومحرر Gutenberg، وتحسين الأداء لمواقع الشركات الكبرى في مصر والخليج العربي.


اترك تعليقاً