useSelect و useDispatch در توسعه بلاک وردپرس چطور کار می‌کنند؟ این دو هوک (Hook) کلیدی که در پکیج @wordpress/data قرار دارند، پل ارتباطی میان کامپوننت‌های ری‌اکت (React) و storeهای Data API هستند؛ useSelect برای خواندن state و useDispatch برای نوشتن state به کار می‌رود. بدون این دو هوک، دسترسی به state سراسری ویرایشگر بلوک و تغییر آن عملاً غیرممکن است. در این مقاله معماری داخلی، تفاوت‌ها، الگوهای بهینه، اشتباهات رایج و کاربردهای پیشرفته این دو هوک را با مثال‌های عملی بررسی می‌کنیم.

useSelect و useDispatch دو هوک بنیادین در توسعه بلاک‌های گوتنبرگ هستند که به‌ترتیب برای خواندن و نوشتن state در Data API وردپرس استفاده می‌شوند.

useSelect یک selector را در یک کامپوننت React subscribe می‌کند و با هر تغییر state، کامپوننت را دوباره رندر می‌نماید.

useDispatch یک یا چند action creator را در اختیار کامپوننت قرار می‌دهد تا بتواند state را به‌صورت کنترل‌شده تغییر دهد.

درک عمیق این دو هوک، تفاوت میان یک توسعه‌دهنده بلاک معمولی و یک توسعه‌دهنده حرفه‌ای گوتنبرگ است.

این مقاله با مثال‌های واقعی، جدول مقایسه، اشتباهات رایج و نکات پیشرفته، تصویری کامل از کارکرد این دو هوک ارائه می‌دهد.

در نخستین بلاکی که برای یک پروژه واقعی می‌ساختم، نیاز داشتم لیست بلاک‌های موجود در ویرایشگر را بخوانم و تعداد آن‌ها را نمایش دهم. تصور می‌کردم باید مستقیماً به store دسترسی پیدا کنم، اما به‌سرعت متوجه شدم که useSelect دقیقاً برای همین کار طراحی شده است. آن تجربه نقطه شروعی شد برای بررسی عمیق‌تر این دو هوک و نقش آن‌ها در توسعه بلاک‌های حرفه‌ای. آنچه در ادامه می‌خوانید حاصل کار عملی با این هوک‌ها در پروژه‌های واقعی است.

useSelect و useDispatch چیستند و چرا به وجود آمدند؟

useSelect و useDispatch دو هوک (Hook) هستند که در پکیج @wordpress/data قرار دارند و برای تعامل با Data API وردپرس طراحی شده‌اند. این دو هوک در واقع لایه انتزاعی روی توابع select و dispatch هستند که امکان استفاده از آن‌ها را در کامپوننت‌های تابعی (Functional Components) ری‌اکت فراهم می‌کنند. برای آشنایی با مبانی Data API، مقاله Data API در وردپرس چیست و چطور state بلاک‌ها را مدیریت می‌کند؟ را مطالعه کنید.

پیش از معرفی این هوک‌ها، توسعه‌دهندگان برای دسترسی به state ویرایشگر مجبور بودند از کامپوننت‌های کلاسی (Class Components) و متد withSelect استفاده کنند. این رویکرد نه‌تنها کد را پیچیده‌تر می‌کرد، بلکه با روند مدرن ری‌اکت که بر کامپوننت‌های تابعی و هوک‌ها تأکید دارد، سازگاری نداشت. useSelect و useDispatch با ارائه یک رابط ساده و declarative، این مشکل را حل کردند. مقاله چرا React Hooks نحوه نوشتن کامپوننت‌ها را متحول کرد؟ به این تحول می‌پردازد.

useSelect و useDispatch یک انتخاب لوکس نیستند؛ زمانی که با state سراسری ویرایشگر کار می‌کنید، به یک ضرورت تبدیل می‌شوند.

نکته مهمی که در بررسی‌های خود به آن پی بردم این است که این دو هوک برخلاف توابع ساده، به چرخه حیات (Lifecycle) کامپوننت React متصل هستند. useSelect به‌طور خودکار کامپوننت را در صورت تغییر state مربوطه دوباره رندر می‌کند و useDispatch به‌طور خودکار actionها را در context مناسب dispatch می‌نماید. این رفتار، مدیریت state را بسیار ساده‌تر از رویکردهای دستی می‌کند. برای مطالعه بیشتر درباره چرخه حیات، مقاله React از صفر: ساخت رابط‌های کاربری تعاملی را ببینید.

جایگاه این دو هوک در معماری Data API

برای درک عمیق useSelect و useDispatch، باید جایگاه آن‌ها را در معماری کلی Data API بررسی کنیم. Data API از چندین لایه تشکیل شده است که هر یک نقش مشخصی در مدیریت state ایفا می‌کند و این دو هوک در بالاترین لایه قرار دارند.

لایه Registry و Store

در پایین‌ترین لایه، Registry قرار دارد که تمام storeها در آن ثبت می‌شوند. هر store شامل state، reducer، actionها و selectorهای مخصوص خود است. این لایه توسط هسته وردپرس یا توسعه‌دهنده افزونه تعریف می‌شود و مستقل از کامپوننت‌های React عمل می‌کند. برای مطالعه بیشتر درباره ساختار store، مقاله ساختار استاندارد افزونه وردپرس چیست و چطور یک پلاگین حرفه‌ای بسازیم؟ را ببینید.

لایه توابع select و dispatch

در لایه میانی، توابع select و dispatch قرار دارند که امکان دسترسی مستقیم به storeها را فراهم می‌کنند. این توابع در هر جایی از کد قابل استفاده هستند، اما در کامپوننت‌های React به‌تنهایی کافی نیستند زیرا نمی‌توانند به تغییرات state واکنش نشان دهند.

لایه هوک‌ها

در بالاترین لایه، هوک‌های useSelect و useDispatch قرار دارند که توابع لایه میانی را با چرخه حیات کامپوننت React ادغام می‌کنند. این لایه است که به کامپوننت‌ها اجازه می‌دهد به‌صورت reactive با state تعامل داشته باشند. برای مطالعه بیشتر درباره معماری گوتنبرگ، مقاله Gutenberg چیست و چگونه ویرایش محتوای وردپرس را متحول کرد؟ را ببینید.

لایه مسئولیت مثال
Registry ثبت و نگهداری storeها register( store )
Store نگهداری state و منطق تغییر createReduxStore()
select / dispatch دسترسی مستقیم به state select( store ).getBlocks()
useSelect / useDispatch اتصال به چرخه حیات React useSelect( ( select ) => ... )

useSelect: از selector تا رندر مجدد

useSelect یک هوک است که یک تابع callback دریافت می‌کند و آن را در context کامپوننت اجرا می‌نماید. این callback یک شیء از selectorها را دریافت می‌کند و باید مقداری را بازگرداند که کامپوننت به آن نیاز دارد. useSelect سپس به تغییرات state مربوطه گوش می‌دهد و در صورت لزوم کامپوننت را دوباره رندر می‌کند.

ساختار پایه useSelect

import { useSelect } from '@wordpress/data';
import { store as blockEditorStore } from '@wordpress/block-editor';

const Edit = () => {
  const blockCount = useSelect( ( select ) => {
    return select( blockEditorStore ).getBlockCount();
  }, [] );

  return <p>تعداد بلاک‌ها: { blockCount }</p>;
};

در این کد، useSelect یک callback دریافت می‌کند که selector getBlockCount را از store ویرایشگر بلوک فراخوانی می‌کند. آرایه دوم، آرایه وابستگی‌ها (Dependency Array) است که تعیین می‌کند callback چه زمانی باید دوباره اجرا شود. اگر این آرایه خالی باشد، callback تنها یک بار در زمان mount اجرا می‌شود. برای مطالعه بیشتر درباره مفاهیم پیشرفته جاوااسکریپت، مقاله مفاهیم پیشرفته جاوااسکریپت که هر توسعه‌دهنده‌ای باید بلد باشد را ببینید.

بازگشت شیء از useSelect

useSelect می‌تواند یک شیء نیز بازگرداند که شامل چندین مقدار باشد. این الگو در بلاک‌های پیچیده بسیار رایج است:

const { blocks, selectedBlock, postType } = useSelect( ( select ) => {
  const { getBlocks, getSelectedBlock } = select( blockEditorStore );
  const { getCurrentPostType } = select( 'core/editor' );
  return {
    blocks: getBlocks(),
    selectedBlock: getSelectedBlock(),
    postType: getCurrentPostType(),
  };
}, [] );

نکته مهم این است که useSelect از یک مکانیزم مقایسه برای تشخیص تغییرات استفاده می‌کند. اگر مقدار بازگشتی یک شیء باشد و در هر رندر یک شیء جدید تولید شود، ممکن است باعث رندرهای غیرضروری شود. برای جلوگیری از این مشکل، از هوک useShallowEqual یا کتابخانه‌هایی مانند equivalent-key-map استفاده می‌شود. برای مطالعه بیشتر درباره بهینه‌سازی، مقاله بهینه سازی جاوااسکریپت: قاتلان خاموشی که در پروژه‌ها دیده‌ام را ببینید.

Resolver و بارگذاری async

useSelect به‌طور خودکار resolverها را فراخوانی می‌کند. اگر selector مورد نظر داده‌ای را برگرداند که هنوز بارگذاری نشده است، resolver مربوطه اجرا می‌شود و پس از تکمیل، state به‌روزرسانی شده و کامپوننت دوباره رندر می‌شود. برای مطالعه بیشتر درباره resolverها، مقاله Data API در وردپرس چیست و چطور state بلاک‌ها را مدیریت می‌کند؟ را ببینید.

useSelect یک پل زنده است میان state سراسری و کامپوننت؛ هر تغییری در state، از طریق این پل به کامپوننت می‌رسد.

useDispatch: از action تا تغییر state

useDispatch یک هوک است که یک یا چند action creator را از یک store دریافت کرده و آن‌ها را به‌صورت آماده فراخوانی در اختیار کامپوننت قرار می‌دهد. برخلاف useSelect که برای خواندن state است، useDispatch برای نوشتن state به کار می‌رود.

ساختار پایه useDispatch

import { useDispatch } from '@wordpress/data';
import { store as blockEditorStore } from '@wordpress/block-editor';

const Edit = () => {
  const { insertBlocks, removeBlocks } = useDispatch( blockEditorStore );

  return (
    <button onClick={ () => insertBlocks( createBlock( 'core/paragraph' ) ) }>
      درج بلاک جدید
    </button>
  );
};

در این کد، useDispatch یک شیء از action creatorها را بازمی‌گرداند که می‌توان آن‌ها را مستقیماً فراخوانی کرد. هر فراخوانی، action مربوطه را در store dispatch می‌کند و reducer state را به‌روزرسانی می‌نماید.

دسترسی به action creatorها

useDispatch می‌تواند action creatorهای یک store را به‌صورت گروهی یا انتخابی دریافت کند. برای دریافت تمام action creatorها، کافی است که نام store را پاس دهید. برای دریافت انتخابی، می‌توانید از الگوی destructuring استفاده کنید:

const { insertBlocks, removeBlocks, updateBlockAttributes } = useDispatch( blockEditorStore );

این رویکرد به شما امکان می‌دهد تنها actionهای مورد نیاز را دریافت کنید و از بارگذاری غیرضروری جلوگیری نمایید. برای مطالعه بیشتر درباره مدیریت افزونه، مقاله چگونه یک افزونه حرفه‌ای وردپرس بسازیم؟ را ببینید.

dispatch در خارج از کامپوننت

در برخی موارد، ممکن است نیاز داشته باشید که از خارج از کامپوننت نیز action dispatch کنید. برای این کار، می‌توانید از تابع dispatch به‌صورت مستقیم استفاده کنید:

import { dispatch } from '@wordpress/data';
import { store as noticesStore } from '@wordpress/notices';

dispatch( noticesStore ).createNotice(
  'success',
  'عملیات با موفقیت انجام شد.'
);

این الگو در callbackهای async یا در توابع کمکی (Helper Functions) مفید است. اما توجه داشته باشید که استفاده از useDispatch در کامپوننت‌ها توصیه می‌شود زیرا به‌طور خودکار با چرخه حیات هماهنگ می‌شود.

مقایسه useSelect و useDispatch

برای درک بهتر تفاوت‌های این دو هوک، بیایید آن‌ها را در چند معیار کلیدی مقایسه کنیم. جدول زیر خلاصه‌ای از این مقایسه را نشان می‌دهد:

معیار useSelect useDispatch
هدف خواندن state از store نوشتن state به store
ورودی تابع callback با پارامتر select نام store یا شیء selector
خروجی مقدار یا شیء از state شیء از action creatorها
رندر مجدد بله، در صورت تغییر state خیر، فقط action dispatch می‌کند
آرایه وابستگی الزامی است ندارد
کاربرد اصلی نمایش داده در کامپوننت تغییر state در پاسخ به رویداد

همان‌طور که جدول نشان می‌دهد، این دو هوک مکمل یکدیگرند. useSelect داده را می‌خواند و useDispatch آن را تغییر می‌دهد. در یک بلاک حرفه‌ای، معمولاً هر دو هوک در کنار یکدیگر استفاده می‌شوند. برای مطالعه بیشتر درباره ری‌اکت، مقاله چرا React Hooks نحوه نوشتن کامپوننت‌ها را متحول کرد؟ را ببینید.

کار با storeهای هسته وردپرس

وردپرس مجموعه‌ای از storeهای آماده را در اختیار توسعه‌دهندگان قرار می‌دهد که هر یک برای هدف خاصی طراحی شده‌اند. آشنایی با این storeها و selectorها و actionهای آن‌ها برای کار حرفه‌ای با useSelect و useDispatch ضروری است.

store core/block-editor

این store مرکزی‌ترین store برای مدیریت بلاک‌هاست و selectorها و actionهای متعددی را ارائه می‌دهد. مهم‌ترین selectorهای آن عبارتند از:

  • getBlocks(): دریافت لیست تمام بلاک‌ها
  • getSelectedBlock(): دریافت بلاک انتخاب‌شده
  • getBlockCount(): دریافت تعداد بلاک‌ها
  • getBlockParents(): دریافت والدهای یک بلاک

و مهم‌ترین actionهای آن:

  • insertBlocks(): درج یک یا چند بلاک
  • removeBlocks(): حذف یک یا چند بلاک
  • updateBlockAttributes(): به‌روزرسانی ویژگی‌های یک بلاک
  • moveBlocksUp() / moveBlocksDown(): جابه‌جایی بلاک‌ها

برای مطالعه بیشتر درباره InnerBlocks و ارتباط آن با این store، مقاله چرا InnerBlocks در وردپرس کلید ساخت بلاک‌های حرفه‌ای است؟ را ببینید.

store core/editor

این store اطلاعات پست جاری را مدیریت می‌کند. selectorهایی مانند getCurrentPostId()، getEditedPostAttribute() و getCurrentPostType() در این store قرار دارند. actionهایی مانند editPost() و savePost() نیز برای تغییر و ذخیره پست استفاده می‌شوند.

store core/notices

این store برای مدیریت اعلان‌ها (Notices) در ویرایشگر استفاده می‌شود. action createNotice() یک اعلان جدید ایجاد می‌کند و selector getNotices() لیست اعلان‌های موجود را بازمی‌گرداند. این store به‌ویژه برای نمایش پیام‌های موفقیت یا خطا در بلاک‌ها مفید است.

store core

این store دسترسی به داده‌های هسته وردپرس را فراهم می‌کند. selectorهایی مانند getPostTypes()، getTaxonomies() و getEntityRecords() در این store قرار دارند. برای مطالعه بیشتر درباره REST API، مقاله آموزش استفاده از REST API در وردپرس را ببینید.

مثال عملی: ساخت یک بلاک با useSelect و useDispatch

برای درک عملی این دو هوک، بیایید یک بلاک بسازیم که لیست بلاک‌های موجود در ویرایشگر را نمایش دهد و به کاربر امکان حذف آن‌ها را بدهد. این بلاک از useSelect برای خواندن لیست بلاک‌ها و از useDispatch برای حذف آن‌ها استفاده می‌کند.

import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps } from '@wordpress/block-editor';
import { useSelect, useDispatch } from '@wordpress/data';
import { store as blockEditorStore } from '@wordpress/block-editor';
import { __ } from '@wordpress/i18n';

registerBlockType( 'my-plugin/block-manager', {
  title: __( 'Block Manager', 'my-plugin' ),
  icon: 'list-view',
  category: 'widgets',
  edit: () => {
    const blockProps = useBlockProps();

    const blocks = useSelect( ( select ) => {
      return select( blockEditorStore ).getBlocks();
    }, [] );

    const { removeBlocks } = useDispatch( blockEditorStore );

    return (
      <div { ...blockProps }>
        <h3>{ __( 'لیست بلاک‌ها', 'my-plugin' ) }</h3>
        <ul>
          { blocks.map( ( block ) => (
            <li key={ block.clientId }>
              { block.name } ({ block.clientId.slice( 0, 8 ) })
              <button
                onClick={ () => removeBlocks( block.clientId ) }
              >
                حذف
              </button>
            </li>
          ) ) }
        </ul>
      </div>
    );
  },
  save: () => {
    return null;
  },
} );

در این کد، useSelect لیست بلاک‌ها را از store ویرایشگر می‌خواند و useDispatch تابع removeBlocks را برای حذف بلاک‌ها در اختیار قرار می‌دهد. توجه کنید که این بلاک نیازی به ذخیره‌سازی محتوا ندارد و تابع save مقدار null بازمی‌گرداند.

ترکیب useSelect و useDispatch، امکان ساخت بلاک‌هایی را فراهم می‌کند که نه‌تنها محتوای خود را نمایش می‌دهند، بلکه بر سایر بلاک‌های ویرایشگر نیز تأثیر می‌گذارند.

برای مطالعه بیشتر درباره ساخت بلاک‌های سفارشی، مقاله آموزش ساخت بلوک سفارشی گوتنبرگ را ببینید.

بهینه‌سازی عملکرد و جلوگیری از رندرهای اضافی

یکی از چالش‌های اصلی در استفاده از useSelect، مدیریت رندرهای غیرضروری است. اگر selector شما یک شیء یا آرایه جدید در هر رندر بازگرداند، کامپوننت ممکن است بی‌دلیل دوباره رندر شود. در ادامه به بررسی راه‌حل‌های این مشکل می‌پردازیم.

آرایه وابستگی و استفاده صحیح از آن

آرایه وابستگی در useSelect تعیین می‌کند که callback چه زمانی دوباره اجرا شود. اگر این آرایه خالی باشد، callback تنها یک بار اجرا می‌شود. اگر شامل متغیرهایی باشد، callback در صورت تغییر آن‌ها دوباره اجرا می‌شود. انتخاب نادرست این آرایه می‌تواند به رندرهای اضافی یا عدم به‌روزرسانی state منجر شود.

const postId = useSelect( ( select ) => {
  return select( 'core/editor' ).getCurrentPostId();
}, [] );

const postTitle = useSelect( ( select ) => {
  return select( 'core/editor' ).getEditedPostAttribute( 'title' );
}, [] );

در این کد، هر دو useSelect از آرایه وابستگی خالی استفاده می‌کنند زیرا به متغیر خارجی وابسته نیستند. این رویکرد از رندرهای غیرضروری جلوگیری می‌کند.

استفاده از useShallowEqual

برای مقادیری که به‌صورت شیء یا آرایه بازگردانده می‌شوند، می‌توانید از هوک useShallowEqual استفاده کنید. این هوک از یک مقایسه سطحی (Shallow Comparison) برای تشخیص تغییرات استفاده می‌کند و از رندرهای غیرضروری جلوگیری می‌نماید.

import { useSelect } from '@wordpress/data';
import { useShallowEqual } from '@wordpress/compose';

const { blocks, selectedBlock } = useSelect( ( select ) => {
  return {
    blocks: select( blockEditorStore ).getBlocks(),
    selectedBlock: select( blockEditorStore ).getSelectedBlock(),
  };
}, [] );

توجه داشته باشید که استفاده از useShallowEqual تنها زمانی مفید است که مقایسه سطحی کافی باشد. برای مقایسه عمیق‌تر، باید از کتابخانه‌های تخصصی استفاده کنید. برای مطالعه بیشتر درباره بهینه‌سازی، مقاله بهینه سازی جاوااسکریپت: قاتلان خاموشی که در پروژه‌ها دیده‌ام را ببینید.

مقایسه انتخابگرها

اگر selector شما داده‌ای را از چند store می‌خواند، می‌توانید از تکنیک مقایسه انتخابگرها (Selector Memoization) استفاده کنید. این تکنیک از کتابخانه‌هایی مانند reselect یا توابع memoize داخلی وردپرس بهره می‌برد.

اشتباهات رایج در استفاده از این دو هوک

در کار با useSelect و useDispatch، توسعه‌دهندگان اغلب مرتکب اشتباهاتی می‌شوند که منجر به کد شکننده یا عملکرد ضعیف می‌شود. در ادامه به برخی از مهم‌ترین این اشتباهات اشاره می‌کنیم.

عدم استفاده از آرایه وابستگی

یکی از رایج‌ترین اشتباهات، عدم استفاده از آرایه وابستگی در useSelect است. اگر این آرایه را حذف کنید، callback در هر رندر اجرا می‌شود و این می‌تواند به کاهش شدید عملکرد منجر شود. همیشه آرایه وابستگی را به‌درستی تعریف کنید.

بازگرداندن شیء جدید در هر رندر

اشتباه دیگر، بازگرداندن یک شیء یا آرایه جدید در هر رندر است. این کار باعث می‌شود که useSelect تغییر state را تشخیص ندهد و کامپوننت را بی‌دلیل دوباره رندر کند. برای جلوگیری از این مشکل، از useShallowEqual یا memoization استفاده کنید.

استفاده از useDispatch در تابع save

تابع save یک بلاک نباید از useDispatch استفاده کند زیرا این تابع در محیطی غیر از کامپوننت React اجرا می‌شود. اگر نیاز به dispatch action در زمان ذخیره‌سازی دارید، باید از تابع dispatch به‌صورت مستقیم استفاده کنید. برای مطالعه بیشتر، مقاله هوک‌های وردپرس: قلب تپنده توسعه را ببینید.

نادیده گرفتن خطاهای async

اگر selector شما به یک resolver وابسته باشد که داده را از REST API بارگذاری می‌کند، باید خطاهای احتمالی را مدیریت کنید. استفاده از hasFinishedResolution برای بررسی وضعیت بارگذاری و getResolutionError برای دریافت خطا، ضروری است.

const { posts, isLoading } = useSelect( ( select ) => {
  const { getEntityRecords, hasFinishedResolution } = select( 'core' );
  const query = { per_page: 10 };
  return {
    posts: getEntityRecords( 'postType', 'post', query ),
    isLoading: ! hasFinishedResolution( 'getEntityRecords', [
      'postType',
      'post',
      query,
    ] ),
  };
}, [] );

استفاده بیش از حد از useDispatch

هر بار که useDispatch فراخوانی می‌شود، یک اتصال جدید به store ایجاد می‌شود. اگر این کار را در چندین کامپوننت تکرار کنید، می‌تواند به کاهش عملکرد منجر شود. بهتر است که useDispatch را در سطح بالاترین کامپوننت فراخوانی کرده و actionها را به فرزندان پاس دهید.

عیب‌یابی مشکلات useSelect و useDispatch

عیب‌یابی مسائل مربوط به این دو هوک نیازمند رویکردی سیستماتیک است. در ادامه به بررسی برخی از مشکلات رایج و راه‌حل‌های آن‌ها می‌پردازیم.

state به‌روزرسانی نمی‌شود

اگر state به‌روزرسانی نمی‌شود، ابتدا بررسی کنید که آیا action به‌درستی dispatch می‌شود. می‌توانید از ابزارهای توسعه‌دهنده Redux DevTools برای مشاهده actionهای dispatch شده و تغییرات state استفاده کنید. سپس بررسی کنید که آیا reducer مربوطه state جدید را بازمی‌گرداند.

کامپوننت دوباره رندر نمی‌شود

اگر کامپوننت در پاسخ به تغییر state دوباره رندر نمی‌شود، بررسی کنید که آیا selector شما به‌درستی به state متصل شده است. همچنین، بررسی کنید که آیا از useShallowEqual استفاده می‌کنید و آیا مقدار بازگشتی به‌درستی مقایسه می‌شود.

خطای "Cannot read property of undefined"

این خطا معمولاً زمانی رخ می‌دهد که selector شما به داده‌ای دسترسی پیدا می‌کند که هنوز بارگذاری نشده است. برای رفع این مشکل، از مقادیر پیش‌فرض یا بررسی‌های null استفاده کنید:

const blocks = useSelect( ( select ) => {
  return select( blockEditorStore ).getBlocks() || [];
}, [] );

پرسش‌های پرتکرار درباره useSelect و useDispatch

تفاوت useSelect با select چیست؟

تابع select یک تابع ساده است که state را می‌خواند اما به تغییرات آن واکنش نشان نمی‌دهد. useSelect یک هوک React است که به تغییرات state گوش می‌دهد و کامپوننت را دوباره رندر می‌کند.

آیا می‌توانم از useSelect و useDispatch در یک کامپوننت استفاده کنم؟

بله، این دو هوک مکمل یکدیگرند و استفاده همزمان از آن‌ها در یک بلاک رایج است.

آیا useSelect بر عملکرد سایت تأثیر می‌گذارد؟

useSelect تنها در ویرایشگر بلوک استفاده می‌شود و بر سرعت فرانت‌اند تأثیری ندارد. با این حال، استفاده نادرست از آن می‌تواند سرعت ویرایشگر را کاهش دهد.

چگونه می‌توانم از رندرهای غیرضروری جلوگیری کنم؟

از آرایه وابستگی به‌درستی استفاده کنید، از useShallowEqual برای مقایسه سطحی بهره ببرید و مقادیر بازگشتی را به‌صورت memoize نگه دارید.

آیا می‌توانم از useSelect برای بارگذاری داده از REST API استفاده کنم؟

بله، useSelect به‌طور خودکار resolverها را فراخوانی می‌کند و می‌تواند داده را از REST API بارگذاری کند. برای مطالعه بیشتر، مقاله REST API در وردپرس راهنمای کامل را ببینید.

آیا useDispatch می‌تواند actionهای async را dispatch کند؟

بله، useDispatch از actionهای async پشتیبانی می‌کند. برای این کار، action creator باید یک تابع async بازگرداند که در نهایت action مربوطه را dispatch می‌کند.

چگونه می‌توانم store سفارشی خودم را با useSelect و useDispatch مدیریت کنم؟

ابتدا store را با createReduxStore تعریف و با register ثبت کنید. سپس می‌توانید از useSelect و useDispatch با نام store سفارشی خود استفاده کنید. برای مطالعه بیشتر، مقاله Data API در وردپرس چیست و چطور state بلاک‌ها را مدیریت می‌کند؟ را ببینید.

آیا استفاده از useSelect در تابع save مجاز است؟

خیر، useSelect و useDispatch تنها در کامپوننت‌های React قابل استفاده هستند و تابع save یک تابع ساده است. برای دسترسی به state در تابع save، باید از توابع select و dispatch به‌صورت مستقیم استفاده کنید.

آیا useSelect از TypeScript پشتیبانی می‌کند؟

بله، useSelect و useDispatch دارای type definition هستند و می‌توانید از آن‌ها در پروژه‌های TypeScript استفاده کنید. برای مطالعه بیشتر، مقاله چرا TypeScript برای توسعه دهندگان جاوااسکریپت یک تحول بنیادی است؟ را ببینید.

تحلیل معماری در سطح پیشرفته

از منظر معماری نرم‌افزار، useSelect و useDispatch یک پیاده‌سازی از الگوی Observer در سطح کامپوننت هستند. در این الگو، کامپوننت نقش Observer را ایفا می‌کند و store نقش Subject را دارد. هر تغییری در Subject، به‌طور خودکار در Observerها منعکس می‌شود. این الگو در سیستم‌های reactive بسیار رایج است و در وردپرس با استفاده از مکانیزم‌های React پیاده‌سازی شده است.

یکی از جنبه‌های کمتر شناخته‌شده، نحوه مدیریت context در useSelect است. زمانی که useSelect فراخوانی می‌شود، یک subscription جدید در registry ایجاد می‌شود. این subscription به تغییرات store گوش می‌دهد و در صورت تغییر، callback مربوطه را اجرا می‌کند. تعداد این subscriptionها می‌تواند بر عملکرد تأثیر بگذارد و به همین دلیل توصیه می‌شود که useSelect را در سطح بالاترین کامپوننت ممکن فراخوانی کنید. برای مطالعه بیشتر درباره معماری وردپرس، مقاله چرا قالب WordPress از نگاه Developer یک معماری است؟ را ببینید.

از دیدگاه عملکرد، useSelect و useDispatch یک trade-off میان سادگی و کارایی ایجاد می‌کنند. استفاده از این هوک‌ها کد را ساده‌تر و خواناتر می‌کند، اما در صورت استفاده نادرست می‌تواند به کاهش عملکرد منجر شود. این trade-off در بلاک‌های پیچیده که چندین selector و action دارند، محسوس‌تر است.

از منظر قابلیت آزمون‌پذیری (Testability)، useSelect و useDispatch امکان تست واحد کامپوننت‌ها را فراهم می‌کنند. با استفاده از کتابخانه‌هایی مانند @testing-library/react، می‌توانید behavior کامپوننت را در پاسخ به تغییرات state تست کنید. این ویژگی به‌ویژه در پروژه‌های بزرگ که چندین توسعه‌دهنده دارند، حیاتی است.

برای مطالعه بیشتر درباره مفاهیم پیشرفته React، می‌توانید صفحه React را در ویکی‌پدیا ببینید.

آینده useSelect و useDispatch

تیم هسته وردپرس در حال کار بر روی بهبود این دو هوک است. از جمله تحولات مورد انتظار می‌توان به پشتیبانی بهتر از TypeScript، بهبود عملکرد در سناریوهای پیچیده و ادغام بهتر با Interactivity API اشاره کرد. این تحولات می‌توانند useSelect و useDispatch را به ابزارهای قدرتمندتری برای ساخت بلاک‌های حرفه‌ای تبدیل کنند. برای مطالعه بیشتر، مقاله آیا WordPress Interactivity API جایگزین React در وردپرس می‌شود؟ را ببینید.

نکات کلیدی

  • useSelect برای خواندن state از store و useDispatch برای نوشتن state به کار می‌رود.
  • این دو هوک برخلاف توابع ساده select و dispatch، به چرخه حیات کامپوننت React متصل هستند.
  • آرایه وابستگی در useSelect تعیین می‌کند که callback چه زمانی دوباره اجرا شود.
  • استفاده از useShallowEqual از رندرهای غیرضروری جلوگیری می‌کند.
  • storeهای هسته وردپرس مانند core/block-editor و core/editor پرکاربردترین storeها هستند.
  • ترکیب useSelect و useDispatch امکان ساخت بلاک‌هایی را فراهم می‌کند که بر سایر بلاک‌ها تأثیر می‌گذارند.
  • همیشه خطاهای async را مدیریت کنید و از hasFinishedResolution استفاده نمایید.
  • useDispatch را در سطح بالاترین کامپوننت فراخوانی کنید تا از ایجاد subscriptionهای متعدد جلوگیری شود.

اگر در پروژه‌ای از useSelect و useDispatch استفاده کرده‌اید، برایم جالب است بدانید کدام بخش آن بیشترین چالش را برای شما ایجاد کرده است. تجربه خود را در دیدگاه‌ها بنویسید؛ به‌خصوص اگر راه‌حل خلاقانه‌ای برای بهینه‌سازی عملکرد یا مدیریت state پیچیده پیدا کرده‌اید که می‌تواند برای دیگران مفید باشد. 🚀