Data API در وردپرس چیست و چطور state بلاک‌ها را مدیریت می‌کند؟

Data API در وردپرس یک لایه مدیریت state مبتنی بر معماری Redux است که برای هماهنگ‌سازی بلاک‌های گوتنبرگ و کامپوننت‌های ری‌اکت در ویرایشگر بلوک طراحی شده است. این API با ارائه storeهای modular، selectorها، actionها و resolverها، جریان داده در ویرایشگر بلوک را کنترل می‌کند. برخلاف REST API که تغییرات را فوراً ذخیره می‌کند، Data API state را در حافظه نگه می‌دارد و تنها با ذخیره پست آن را به سرور می‌فرستد. درک معماری آن برای توسعه‌دهندگان افزونه و قالب ضروری است. این مقاله به بررسی عمیق ساختار، کاربردها و اشتباهات رایج آن می‌پردازد.

وقتی برای اولین بار با ویرایشگر بلوک وردپرس مواجه شدم، چیزی که بیش از همه ذهنم را مشغول کرد این بود که state بلاک‌ها چگونه در سراسر رابط کاربری هماهنگ می‌شود. چطور یک کلیک روی دکمه‌ای در نوار کناری بلافاصله روی پیش‌نمایش بلاک اثر می‌گذارد، بدون آنکه صفحه رفرش شود؟ پاسخ در لایه‌ای نهفته است که Data API نام دارد. این مقاله حاصل کار عملی با این API در پروژه‌های واقعی و بررسی کد منبع گوتنبرگ است.

Data API چیست و چرا وردپرس به آن نیاز دارد؟

Data API که با نام رسمی @wordpress/data شناخته می‌شود، یک کتابخانه مدیریت state در جاوااسکریپت (JavaScript) است که در هسته وردپرس تعبیه شده است. این کتابخانه بر پایه اصول Redux ساخته شده اما به‌عنوان یک پیاده‌سازی مستقیم از آن در نظر گرفته نمی‌شود. تفاوت اصلی در این است که Data API برای همزیستی با APIها و قابلیت‌های خاص وردپرس طراحی شده و از پشتیبانی داخلی برای actionهای asynchronous (ناهمزمان) برخوردار است.

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

Data API یک انتخاب لوکس نیست؛ وقتی ویرایشگر بلوک را جدی می‌گیرید، به یک ضرورت معماری تبدیل می‌شود.

REST API (Representational State Transfer Application Programming Interface) در وردپرس امکان تبادل داده از طریق HTTP را فراهم می‌کند، اما Data API یک لایه بالاتر از آن قرار می‌گیرد و state را در سمت کلاینت مدیریت می‌کند. برای آشنایی با مبانی REST API در وردپرس، مقاله REST API در وردپرس راهنمای کامل را مطالعه کنید.

نکته مهمی که در بررسی‌های خود به آن پی بردم این است که Data API تغییرات را فوراً در پایگاه داده ذخیره نمی‌کند. زمانی که یک action را dispatch می‌کنید، state در حافظه به‌روزرسانی می‌شود اما تا زمانی که پست را ذخیره نکنید، این تغییرات به سرور ارسال نمی‌شوند. این رفتار برای ویرایشگر بلوک حیاتی است زیرا امکان پیش‌نمایش و بازگشت‌پذیری را فراهم می‌کند. اگر به‌جای Data API مستقیماً با REST API کار کنید، هر تغییر بلافاصله ذخیره می‌شود و دیگر امکان انصراف از تغییرات وجود نخواهد داشت. مقاله چرا REST API در WordPress معماری Plugin را بازتعریف کرد؟ به این تمایز معماری می‌پردازد.

معماری هسته Data API

معماری Data API بر پایه چند مفهوم بنیادین بنا شده است که درک هر یک برای کار حرفه‌ای با این API ضروری است. این مفاهیم عبارتند از: Store، Selector، Action، Reducer و Resolver. هر یک از این اجزا نقش مشخصی در جریان داده ایفا می‌کنند و در کنار یکدیگر یک سیستم مدیریت state پیش‌بینی‌پذیر و مقیاس‌پذیر می‌سازند.

Registry (ثبت‌کننده) یک شیء مرکزی است که تمام storeها در آن ثبت می‌شوند. این registry وظیفه دارد storeهای جدید را بپذیرد، دسترسی به storeهای موجود را فراهم کند و اشتراک‌های تغییر state را مدیریت نماید. در وردپرس معمولاً چندین store به‌طور همزمان فعال هستند و هر کدام به یک حوزه خاص اختصاص دارند.

Store: واحد بنیادین state

Store جایی است که state برنامه در آن نگهداری می‌شود و منطق تغییر آن قرار دارد. هر store شامل یک reducer برای مدیریت به‌روزرسانی state، مجموعه‌ای از actionها برای تغییر state، selectorها برای خواندن state و در صورت نیاز resolverها برای بارگذاری داده از منابع خارجی است. Storeها در Data API به‌صورت modular طراحی شده‌اند و هر کدام یک namespace منحصربه‌فرد دارند. برای مطالعه بیشتر درباره ساختار ماژولار در وردپرس، مقاله ساختار استاندارد افزونه وردپرس چیست و چطور یک پلاگین حرفه‌ای بسازیم؟ را ببینید.

Selector: خواندن state

Selector یک تابع است که state را دریافت می‌کند و مقدار مورد نظر را از آن استخراج می‌نماید. selectorها به‌عنوان یک انتزاع روی داده خام عمل می‌کنند و این امکان را فراهم می‌سازند که بدون وابستگی به ساختار داخلی store، به داده دسترسی داشته باشید. در ویرایشگر بلوک، selectorهایی مانند getBlocks() یا getCurrentPostType() به‌طور مکرر استفاده می‌شوند.

Action: نوشتن state

Action یک شیء ساده جاوااسکریپت است که توصیف‌کننده یک تغییر در state می‌باشد. Actionها تنها راه مجاز برای ایجاد تغییر در state هستند. هر action حداقل یک ویژگی type دارد که نوع تغییر را مشخص می‌کند. dispatch کردن یک action باعث می‌شود reducer مربوطه فراخوانی شده و state جدید تولید گردد.

Reducer: منطق تغییر state

Reducer یک تابع خالص (pure function) است که state فعلی و یک action را دریافت می‌کند و state جدید را بازمی‌گرداند. reducerها نباید عوارض جانبی (side effect) داشته باشند و باید تغییرات state را به‌صورت immutable مدیریت کنند. این ویژگی باعث می‌شود ردیابی تغییرات و اشکال‌زدایی بسیار ساده‌تر شود.

Resolver: بارگذاری asynchronous

Resolver یک side effect برای selector است. زمانی که یک selector فراخوانی می‌شود و داده مورد نیاز در state موجود نیست، resolver مربوطه اجرا شده و داده را از یک منبع خارجی مانند REST API بارگذاری می‌کند. پس از تکمیل بارگذاری، action مربوطه dispatch شده و state به‌روزرسانی می‌گردد. برای آشنایی با نحوه استفاده از REST API در وردپرس، مقاله آموزش استفاده از REST API در وردپرس را مطالعه کنید.

Resolverها پلی هستند میان state محلی و داده سرور؛ بدون آن‌ها هر بلاک مجبور است به‌طور مستقل با REST API تعامل کند.

Storeها: قلب تپنده مدیریت state

Storeها در Data API به‌عنوان مخازن state عمل می‌کنند و هر یک مسئولیت یک بخش مشخص از برنامه را بر عهده دارند. برخلاف Redux سنتی که معمولاً یک store واحد دارد، Data API از چندین store مستقل پشتیبانی می‌کند. این طراحی modular باعث می‌شود هر بخش از ویرایشگر بتواند state خود را به‌طور مستقل مدیریت کند و نیازی به یک store عظیم و پیچیده نباشد.

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

Namespace نام کاربرد
core WordPress Core Data تعامل با تنظیمات کلی و داده‌های هسته
core/blocks Block Types Data مدیریت بلاک‌های ثبت‌شده و استایل‌ها
core/block-editor Block Editor Data کنترل ویرایشگر بلوک: درج، حذف و جابه‌جایی بلاک‌ها
core/editor Post Editor Data اطلاعات پست جاری مانند نوع پست و ویژگی‌ها
core/edit-post Editor UI Data مدیریت رابط کاربری ویرایشگر
core/notices Notices Data مدیریت اعلان‌ها در ویرایشگر

برای ثبت یک store سفارشی، از تابع createReduxStore و سپس register استفاده می‌شود. این فرآیند به شما امکان می‌دهد state اختصاصی افزونه یا قالب خود را در registry مرکزی ثبت کنید. مقاله چگونه یک افزونه حرفه‌ای وردپرس بسازیم؟ به مبانی ساخت افزونه می‌پردازد که پیش‌نیاز درک این مبحث است.

کد زیر یک store ساده را تعریف می‌کند:

import { createReduxStore, register } from '@wordpress/data';

const DEFAULT_STATE = {
  prices: {},
  discountPercent: 0,
};

const actions = {
  setPrice( item, price ) {
    return { type: 'SET_PRICE', item, price };
  },
  startSale( discountPercent ) {
    return { type: 'START_SALE', discountPercent };
  },
};

const store = createReduxStore( 'my-shop', {
  reducer( state = DEFAULT_STATE, action ) {
    switch ( action.type ) {
      case 'SET_PRICE':
        return {
          ...state,
          prices: {
            ...state.prices,
            [ action.item ]: action.price,
          },
        };
      case 'START_SALE':
        return {
          ...state,
          discountPercent: action.discountPercent,
        };
    }
    return state;
  },
  actions,
  selectors: {
    getPrice( state, item ) {
      return state.prices[ item ];
    },
    getDiscountPercent( state ) {
      return state.discountPercent;
    },
  },
} );

register( store );

Selectorها: خواندن state

Selectorها توابعی هستند که state را دریافت کرده و بخش مورد نظر را از آن استخراج می‌کنند. در Data API، selectorها معمولاً از طریق تابع select یا هوک useSelect فراخوانی می‌شوند. تابع select یک شیء از selectorهای pre-bound را بازمی‌گرداند که state جاری به‌طور خودکار به آن‌ها پاس داده می‌شود.

در کامپوننت‌های ری‌اکت، استفاده از هوک useSelect توصیه می‌شود زیرا این هوک به‌طور خودکار به تغییرات state گوش می‌دهد و کامپوننت را در صورت لزوم دوباره رندر می‌کند. برای درک عمیق‌تر ری‌اکت و هوک‌ها، مقاله React از صفر: ساخت رابط‌های کاربری تعاملی را مطالعه کنید.

کد زیر نحوه استفاده از useSelect برای خواندن اطلاعات پست جاری را نشان می‌دهد:

import { useSelect } from '@wordpress/data';
import { store as editorStore } from '@wordpress/editor';

const { postType, template } = useSelect( ( select ) => ( {
  postType: select( editorStore ).getCurrentPostType(),
  template: select( editorStore ).getEditedPostAttribute( 'template' ),
} ) );

نکته مهم این است که selectorها نباید عوارض جانبی داشته باشند. آن‌ها تنها باید state را بخوانند و مقدار مورد نظر را بازگردانند. اگر نیاز به بارگذاری داده از سرور دارید، باید از resolver استفاده کنید.

Actionها: نوشتن state

Actionها تنها راه مجاز برای ایجاد تغییر در state هستند. هر action یک شیء ساده است که نوع تغییر را توصیف می‌کند. برای dispatch کردن یک action، از تابع dispatch یا هوک useDispatch استفاده می‌شود.

در ویرایشگر بلوک، actionهای متعددی برای مدیریت بلاک‌ها وجود دارد. برای مثال، insertBlocks برای درج بلاک جدید، removeBlocks برای حذف بلاک و updateBlockAttributes برای تغییر ویژگی‌های یک بلاک استفاده می‌شود. این actionها از طریق store core/block-editor قابل دسترسی هستند.

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

const { insertBlocks } = useDispatch( blockEditorStore );

// درج یک بلاک جدید
insertBlocks( createBlock( 'core/paragraph', {
  content: 'محتوای نمونه',
} ) );
Actionها زبان مشترک بلاک‌ها هستند؛ آن‌ها تغییرات را توصیف می‌کنند بدون آنکه بدانند چه کسی قرار است state را به‌روزرسانی کند.

Reducerها: منطق تغییر state

Reducerها توابعی خالص هستند که state فعلی و یک action را دریافت کرده و state جدید را بازمی‌گردانند. در Data API، reducerها قلب منطق تغییر state هستند. یک reducer باید همیشه یک شیء state جدید بازگرداند و هرگز state موجود را به‌طور مستقیم تغییر ندهد.

الگوی immutable در reducerها به دلایل متعددی اهمیت دارد. اولاً، این الگو امکان ردیابی تغییرات را فراهم می‌کند. ثانیاً، از بروز باگ‌های ناشی از تغییرات ناخواسته در state جلوگیری می‌کند. ثالثاً، با کتابخانه‌هایی مانند React که بر مبنای مقایسه مرجع (reference equality) تصمیم به رندر مجدد می‌گیرند، سازگاری کامل دارد.

برای آشنایی با مفاهیم پیشرفته جاوااسکریپت که درک reducerها را تسهیل می‌کند، مقاله مفاهیم پیشرفته جاوااسکریپت که هر توسعه‌دهنده‌ای باید بلد باشد را ببینید.

Resolverها: بارگذاری async داده

Resolverها یکی از متمایزکننده‌ترین ویژگی‌های Data API نسبت به Redux سنتی هستند. یک resolver یک side effect برای selector است. زمانی که یک selector فراخوانی می‌شود و داده مورد نیاز در state موجود نیست، resolver مربوطه به‌طور خودکار اجرا می‌شود و داده را از یک منبع خارجی بارگذاری می‌کند.

این الگو به‌ویژه برای بارگذاری داده از REST API بسیار مفید است. به‌جای آنکه هر کامپوننت به‌طور مستقل درخواست HTTP ارسال کند، می‌تواند selector مربوطه را فراخوانی کند و resolver به‌طور خودکار داده را از سرور دریافت کرده و در state ذخیره می‌کند.

import apiFetch from '@wordpress/api-fetch';

const resolvers = {
  *getPrice( item ) {
    const prices = yield apiFetch( {
      path: `/my-shop/v1/prices/${ item }`,
    } );
    return {
      type: 'SET_PRICE',
      item,
      price: prices[ item ],
    };
  },
};

نکته مهم این است که resolverها با استفاده از generator functionها پیاده‌سازی می‌شوند. این الگو به Data API اجازه می‌دهد تا جریان asynchronous را به‌صورت declarative مدیریت کند.

مدیریت state بلاک‌ها در عمل

بلاک‌ها در ویرایشگر وردپرس هر یک دارای state خاص خود هستند. این state شامل ویژگی‌های بلاک (attributes)، وضعیت انتخاب (selection state)، و ترتیب قرارگیری در ویرایشگر می‌شود. Data API این state را در چند store مختلف نگهداری می‌کند و امکان هماهنگی میان آن‌ها را فراهم می‌سازد.

هنگامی که یک بلاک در ویرایشگر درج می‌شود، اطلاعات آن در store core/block-editor ذخیره می‌شود. این store یک آرایه از تمام بلاک‌های موجود در ویرایشگر را نگهداری می‌کند. هر بلاک دارای یک شناسه منحصربه‌فرد (clientId) است که برای ارجاع به آن در سراسر سیستم استفاده می‌شود.

ویژگی‌های هر بلاک (attributes) در store core/editor ذخیره می‌شود. زمانی که یک ویژگی تغییر می‌کند، action editPost dispatch می‌شود و reducer مربوطه state را به‌روزرسانی می‌کند. برای مطالعه بیشتر درباره نحوه ساخت بلاک سفارشی، مقاله آموزش ساخت بلوک سفارشی گوتنبرگ را ببینید.

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

در ویرایشگر بلوک، store منبع حقیقت است؛ کامپوننت‌ها تنها نمایندگان آن هستند.

Storeهای هسته وردپرس

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

Store core دسترسی به داده‌های هسته وردپرس را فراهم می‌کند. selectorهایی مانند getPostTypes() و getTaxonomies() در این store قرار دارند. Store core/blocks اطلاعات مربوط به بلاک‌های ثبت‌شده را نگهداری می‌کند و امکان دریافت لیست بلاک‌ها، استایل‌ها و دسته‌بندی‌های آن‌ها را فراهم می‌سازد.

Store core/block-editor مرکزی‌ترین store برای مدیریت بلاک‌هاست. actionهای کلیدی این store عبارتند از:

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

Store core/editor اطلاعات پست جاری را مدیریت می‌کند. selectorهایی مانند getCurrentPostId() و getEditedPostAttribute() در این store قرار دارند. برای درک بهتر نحوه استفاده از این selectorها در قالب، مقاله چرا قالب WordPress از نگاه Developer یک معماری است؟ را مطالعه کنید.

مثال عملی: ساخت یک بلاک با Data API

برای درک عملی Data API، بیایید یک بلاک ساده بسازیم که یک شمارنده را نمایش می‌دهد و state آن از طریق store مدیریت می‌شود. این مثال نشان می‌دهد چگونه می‌توان یک store سفارشی تعریف کرد، selector و action مناسب ساخت و آن را در یک بلاک به کار گرفت.

ابتدا store سفارشی را تعریف می‌کنیم:

import { createReduxStore, register } from '@wordpress/data';

const DEFAULT_STATE = {
  count: 0,
};

const actions = {
  increment() {
    return { type: 'INCREMENT' };
  },
  decrement() {
    return { type: 'DECREMENT' };
  },
  setCount( count ) {
    return { type: 'SET_COUNT', count };
  },
};

const store = createReduxStore( 'my-plugin/counter', {
  reducer( state = DEFAULT_STATE, action ) {
    switch ( action.type ) {
      case 'INCREMENT':
        return { ...state, count: state.count + 1 };
      case 'DECREMENT':
        return { ...state, count: state.count - 1 };
      case 'SET_COUNT':
        return { ...state, count: action.count };
      default:
        return state;
    }
  },
  actions,
  selectors: {
    getCount( state ) {
      return state.count;
    },
  },
} );

register( store );

سپس در کامپوننت بلاک، از هوک‌های useSelect و useDispatch برای تعامل با store استفاده می‌کنیم:

import { useSelect, useDispatch } from '@wordpress/data';
import { store as counterStore } from './store';

const Edit = () => {
  const count = useSelect( ( select ) => {
    return select( counterStore ).getCount();
  }, [] );

  const { increment, decrement } = useDispatch( counterStore );

  return (
    <div>
      <p>شمارنده: { count }</p>
      <button onClick={ increment }>افزایش</button>
      <button onClick={ decrement }>کاهش</button>
    </div>
  );
};

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

مدیریت حالت بارگذاری و خطا

یکی از جنبه‌های مهم کار با Data API، مدیریت حالت بارگذاری (loading state) و خطا (error state) است. زمانی که یک selector فراخوانی می‌شود و resolver مربوطه در حال بارگذاری داده از سرور است، کامپوننت باید حالت بارگذاری را نمایش دهد. همچنین در صورت بروز خطا، باید پیام مناسبی به کاربر نشان داده شود.

Data API یک selector به نام hasFinishedResolution در اختیار توسعه‌دهندگان قرار می‌دهد که مشخص می‌کند آیا resolver مربوط به یک selector خاص تکمیل شده است یا خیر. این selector دو پارامتر دریافت می‌کند: نام selector و آرایه پارامترهای ارسالی به آن.

import { useSelect } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';

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

if ( ! hasResolved ) {
  return <p>در حال بارگذاری...</p>;
}

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

Data API در برابر REST API

یکی از سوالات رایج توسعه‌دهندگان این است که چه زمانی باید از Data API استفاده کنند و چه زمانی از REST API. پاسخ به این سوال به ماهیت پروژه و نیازهای آن بستگی دارد. درک تفاوت‌های این دو رویکرد برای انتخاب معماری صحیح ضروری است.

ویژگی Data API REST API
ذخیره‌سازی تغییرات فقط با ذخیره پست بلافاصله
مدیریت state مرکزی و مبتنی بر Redux بدون state مرکزی
مناسب برای ویرایشگر بلوک و رابط‌های تعاملی اپلیکیشن‌های خارجی و headless
پشتیبانی async داخلی (resolver و thunk) وابسته به پیاده‌سازی کلاینت

به‌طور کلی، اگر در حال توسعه برای ویرایشگر بلوک هستید یا نیاز به مدیریت state پیچیده در سمت کلاینت دارید، Data API انتخاب مناسب‌تری است. اما اگر در حال ساخت یک اپلیکیشن خارجی هستید که باید با وردپرس تعامل کند، REST API رویکرد مستقیم‌تری است. برای مطالعه بیشتر درباره REST API در وردپرس، مقاله REST API در وردپرس راهنمای کامل را ببینید.

اشتباهات رایج

در کار با Data API، توسعه‌دهندگان اغلب مرتکب اشتباهاتی می‌شوند که منجر به کد شکننده، عملکرد ضعیف یا باگ‌های دشوار می‌شود. آشنایی با این اشتباهات می‌تواند از اتلاف وقت و منابع جلوگیری کند.

یکی از رایج‌ترین اشتباهات، استفاده مستقیم از state به‌جای selector است. برخی توسعه‌دهندگان سعی می‌کنند به ساختار داخلی store دسترسی پیدا کنند و آن را مستقیماً تغییر دهند. این رویکرد باعث می‌شود کد آن‌ها به پیاده‌سازی داخلی store وابسته شود و با تغییرات آینده وردپرس از کار بیفتد. رویکرد صحیح استفاده از selectorها و actionهای ارائه‌شده توسط store است.

اشتباه دیگر، نادیده گرفتن immutable بودن state است. reducerها باید همیشه یک شیء state جدید بازگردانند و هرگز state موجود را تغییر ندهند. نقض این اصل منجر به باگ‌هایی می‌شود که ردیابی آن‌ها بسیار دشوار است. برای آشنایی با اصول کدنویسی تمیز در وردپرس، مقاله هوک‌های وردپرس: قلب تپنده توسعه را مطالعه کنید.

اشتباه سوم، عدم مدیریت صحیح resolverهاست. اگر resolver به‌درستی پیاده‌سازی نشود، ممکن است داده‌ها بارگذاری نشوند یا state به‌درستی به‌روزرسانی نگردد. در چنین مواردی، استفاده از hasFinishedResolution برای بررسی وضعیت بارگذاری ضروری است.

بیشتر باگ‌های Data API از دستکاری مستقیم state و نادیده گرفتن الگوهای immutable ناشی می‌شوند.

عیب‌یابی Data API

عیب‌یابی مسائل مربوط به Data API نیازمند رویکردی سیستماتیک است. برخلاف خطاهای ساده جاوااسکریپت، مشکلات Data API اغلب به‌صورت رفتارهای غیرمنتظره در رابط کاربری ظاهر می‌شوند که ردیابی آن‌ها دشوار است.

اولین گام در عیب‌یابی، بررسی state جاری store است. می‌توانید از طریق کنسول مرورگر به store دسترسی پیدا کرده و state آن را مشاهده کنید. برای این کار از تابع select در کنسول استفاده کنید:

wp.data.select( 'core/block-editor' ).getBlocks();

این دستور لیست تمام بلاک‌های موجود در ویرایشگر را نمایش می‌دهد. با بررسی این لیست می‌توانید تشخیص دهید که آیا state به‌درستی به‌روزرسانی شده است یا خیر.

گام دوم، بررسی resolverهاست. می‌توانید از selector hasFinishedResolution برای بررسی وضعیت بارگذاری استفاده کنید. اگر resolver به‌درستی کار نمی‌کند، ممکن است داده‌ها هرگز بارگذاری نشوند یا state در حالت نامعتبر باقی بماند.

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

تحلیل مهندسی پیشرفته

از منظر معماری نرم‌افزار، Data API یک پیاده‌سازی از الگوی Flux است که با نیازهای خاص وردپرس سازگار شده است. برخلاف Redux که یک store واحد دارد، Data API از چندین store مستقل پشتیبانی می‌کند که هر یک می‌تواند به‌طور جداگانه ثبت و مدیریت شود. این طراحی modular امکان جداسازی نگرانی‌ها (separation of concerns) را در سطح state فراهم می‌کند.

یکی از جنبه‌های کمتر شناخته‌شده Data API، مفهوم controlهاست. controlها در نسخه‌های اولیه برای مدیریت جریان‌های asynchronous استفاده می‌شدند، اما امروزه thunkها جایگزین آن‌ها شده‌اند. با این حال، درک controlها برای کار با کدهای قدیمی‌تر ضروری است. یک control یک action خاص را با یک handler مرتبط می‌کند و به reducer اجازه می‌دهد تا عملیات asynchronous را به‌صورت declarative مدیریت کند.

از دیدگاه عملکرد، Data API از یک مکانیزم اشتراک‌گذاری بهینه استفاده می‌کند. زمانی که state تغییر می‌کند، تنها کامپوننت‌هایی که به بخش تغییر‌یافته state وابسته هستند دوباره رندر می‌شوند. این بهینه‌سازی از طریق مقایسه مرجع در selectorها انجام می‌شود. به همین دلیل است که بازگرداندن یک شیء جدید در selector حتی با محتوای یکسان می‌تواند باعث رندر غیرضروری شود.

از منظر قابلیت آزمون‌پذیری (testability)، Data API امکان تست واحد reducerها و selectorها را به‌راحتی فراهم می‌کند. از آنجا که reducerها توابع خالص هستند، می‌توان آن‌ها را بدون نیاز به راه‌اندازی محیط وردپرس تست کرد. selectorها نیز توابعی خالص از state هستند و تست آن‌ها ساده است. این ویژگی‌ها Data API را به یک انتخاب معماری مناسب برای پروژه‌های بزرگ تبدیل می‌کند.

پرسش‌های پرتکرار درباره Data API

آیا Data API جایگزین REST API شده است؟

خیر. این دو API اهداف متفاوتی دارند. REST API برای تعامل با وردپرس از خارج از محیط مرورگر طراحی شده است، در حالی که Data API state را در سمت کلاینت مدیریت می‌کند. در بسیاری از پروژه‌ها، این دو در کنار یکدیگر استفاده می‌شوند.

آیا استفاده از Data API برای پروژه‌های کوچک ضروری است؟

برای پروژه‌های کوچک که تنها از چند بلاک ساده استفاده می‌کنند، ممکن است نیاز به Data API محسوس نباشد. اما به‌محض اینکه تعداد بلاک‌ها افزایش یابد و نیاز به هماهنگی state میان آن‌ها ایجاد شود، Data API به یک ضرورت تبدیل می‌شود.

تفاوت store و state چیست؟

state داده‌ای است که نگهداری می‌شود و store مکانی است که این داده در آن قرار دارد. store همچنین شامل منطق تغییر state (reducer)، actionها و selectorهاست.

آیا می‌توانم store سفارشی خودم را بسازم؟

بله. با استفاده از توابع createReduxStore و register می‌توانید store سفارشی خود را تعریف و در registry مرکزی ثبت کنید. این کار به شما امکان می‌دهد state اختصاصی افزونه یا قالب خود را مدیریت کنید.

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

این مشکل معمولاً به یکی از دلایل زیر رخ می‌دهد: action به‌درستی dispatch نشده است، reducer state جدید را بازنمی‌گرداند، یا کامپوننت به تغییرات state اشتراک (subscribe) نکرده است. بررسی این سه مورد معمولاً منجر به یافتن ریشه مشکل می‌شود.

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

Data API در وردپرس یک لایه ضروری برای مدیریت state در ویرایشگر بلوک است. اگرچه ممکن است در نگاه اول پیچیده به نظر برسد، اما درک مفاهیم پایه آن — store، selector، action، reducer و resolver — به سرعت این پیچیدگی را کاهش می‌دهد. تجربه نشان داده است که توسعه‌دهندگانی که این مفاهیم را به‌درستی درک می‌کنند، می‌توانند بلاک‌ها و افزونه‌های بسیار پایدارتر و مقیاس‌پذیرتری بسازند. اگر در پروژه‌ای با این API کار کرده‌اید، برایم جالب است بدانید کدام بخش آن بیشترین چالش را برای شما ایجاد کرده است. تجربه خود را در دیدگاه‌ها بنویسید؛ به‌خصوص اگر راه‌حل خلاقانه‌ای برای مدیریت state پیچیده پیدا کرده‌اید که می‌تواند برای دیگران مفید باشد. 🚀