بیشتر کدهای فرانت‌اند با کارهای ناهمگام (asynchronous) مانند یک قالب واحد برخورد می‌کنند. شما یک Promise را شروع می‌کنید، منتظر حل شدن (resolve) آن می‌مانید، نتیجه را در وضعیت محلی (local state) می‌ریزید و اجازه می‌دهید فریم‌ورک تفاوت‌ها را تطبیق دهد (reconcile the diff). این الگو وسوسه‌انگیز است زیرا همه جا جواب می‌دهد: یک فراخوانی REST، ارسال یک فرم، یا یک پیام WebSocket. همه آن‌ها در یک useEffect یا هندلر رویداد فرود می‌آیند، همگی از طریق setState عبور می‌کنند و همگی شبیه به زیرساخت‌های ناهمگامِ یکسان به نظر می‌رسند. این یکنواختی یک تله است. در یک اپلیکیشن واقعی، همه کارهای ناهمگام یکسان نیستند. تظاهر به اینکه آن‌ها یکی هستند، کامپوننت‌های UI شما را به معماران داده‌ای تصادفی تبدیل می‌کند که با قلاب‌های useEffect و امید به شانس، سرهم شده‌اند.

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

پرس‌وجوها (Queries): واقعیت‌هایی با یک آدرس

یک پرس‌وجو فقط یک fetch ساده نیست. بلکه درخواستی برای یک واقعیت قابل شناسایی است. شما /user/123 را می‌خواهید، نه "برخی داده‌های کاربر". این تمایز مهم است زیرا هویت همان چیزی است که کش کردن را ممکن می‌سازد. اگر دو کامپوننت در یک صفحه به یک رکورد کاربری یکسان نیاز داشته باشند، باید از یک پاسخ مشترک استفاده کنند. وقتی هر کامپوننت کپی محلی خود را در useState نگه می‌دارد، حقیقت را تکه‌تکه می‌کنید. آواتار در هدر و نام در سایدبار از هم فاصله می‌گیرند، زیرا در زمان‌های مختلف واکشی شده‌اند، یا یکی شکست خورده و دیگری موفق شده است.

یک پرس‌وجو را به عنوان یک منبع (resource) در نظر بگیرید، نه یک عمل (action). پرس‌وجو دارای یک کلید کش (cache key)، یک سیاست تازگی (freshness policy) و چرخه‌ای از حیات است که از هر کامپوننت واحدی طولانی‌تر است. یک لایه پرس‌وجوی خوب می‌داند که خواندن /projects?page=2 با خواندن /projects?page=3 متفاوت است. هر URL و مجموعه پارامترها یک آدرس را تشکیل می‌دهند و داده‌های موجود در آن آدرس می‌تواند قدیمی، تازه یا مفقود باشد. UI نباید مسئول این حساب‌وکتاب‌ها باشد. UI باید از لایه داده برای user:123 درخواست کند و یک اسنپ‌شات دریافت کند. اینکه آن اسنپ‌شات دو ثانیه پیش از سرور آمده یا دو میلی‌ثانیه پیش از یک کش، به کامپوننت مربوط نیست.

پیامدهای عملی آن فوری است. وقتی با هر عملیات خواندن (read) به عنوان یک fetch دستوری (imperative) در داخل یک کامپوننت برخورد می‌کنید، قابلیت حذف داده‌های تکراری (deduplication) را از دست می‌دهید. قابلیت بازنشانی در پس‌زمینه (background refresh) را از دست می‌دهید. قابلیت نمایش فوری داده‌های کش‌شده در حین اعتبارسنجی آن‌ها در پس‌زمینه را از دست می‌دهید. یک پرس‌وجو شایسته مکانی خارج از درخت UI شماست.

تغییرات (Mutations): تغییر دادن جهان

اگر پرس‌وجوها درباره جهان سوال می‌کنند، تغییرات (mutations) آن را تغییر می‌دهند. درخواست شبکه واقعی — یعنی POST ،PUT یا DELETE — معمولاً بخش آسان کار است. بخش سخت، هر چیزی است که پس از گفتن "OK" توسط سرور اتفاق می‌افتد.

فرض کنید کاربری نام نمایشی خود را به‌روزرسانی می‌کند. خودِ تغییر (mutation) تنها یک درخواست واحد است. اما شعاع اثر (blast radius) آن همه جا هست. صفحه پروفایل نام قدیمی را نگه می‌دارد. نوار ناوبری نام قدیمی را نشان می‌دهد. تاریخچه نظرات ممکن است به آن ارجاع دهد. اگر کد تغییر شما کاری جز تغییر یک پرچم isLoading محلی و سپس به‌روزرسانی یک تکه از وضعیت انجام ندهد، اپلیکیشن شما در حال دروغ گفتن به خودش است. برخی از گوشه‌های UI وانمود می‌کنند که تغییر انجام شده است، در حالی که بقیه اصلاً نمی‌دانند چیزی تغییر کرده است.

یک تغییر (mutation) باید تأثیر خود را بر گراف داده اعلام کند. باید به سیستم بگوید کدام پرس‌وجوها اکنون نامعتبر هستند، کدام کلیدهای کش باید دوباره واکشی شوند و کدام روابط تغییر کرده‌اند. این اساساً با یک پرس‌وجو متفاوت است. یک پرس‌وجو فقط‌خواندنی (read-only) و قابل اشتراک‌گذاری است. یک تغییر، تمرکز بر نوشتن (write-focused) دارد و برای کش‌های موجود مخرب است. تخت کردن آن‌ها در یک انتزاع (abstraction) واحد به این معناست که توسعه‌دهندگان در نهایت مجبور می‌شوند به صورت دستی refetch() را در کامپوننت‌های تصادفی فراخوانی کنند، یا بدتر از آن، از قلاب‌های useEffect در سراسر درخت استفاده کنند تا وضعیت محلی را دوباره با سرور همگام (sync) کنند.

مدل مالکیت نیز متفاوت است. پرس‌وجوها معمولاً متعلق به کش هستند. یک تغییر متعلق به عمل کاربری است که آن را تحریک کرده است. تغییر دارای یک وضعیت در حال انتظار (pending state)، یک وضعیت خطا (error state) و احتمالاً یک مقدار خوش‌بینانه (optimistic value) است که نیاز به بازگشت (roll