برنامه نویسی

React Server Model (RSM) v0.3.0 منتشر شد!

مقالات قبلی

TL; DR

RSM یک کتابخانه مدیریت وضعیت سرور جدید برای React است. این به توسعه‌دهنده اجازه می‌دهد تا داده‌ها را مانند استفاده از Redux سفارشی کند، در حالی که یک api با استفاده آسان مانند React Query را نیز ارائه می‌کند. useQuery. آن را امتحان کنید!

سلام به همه، انگار از آخرین باری که با هم صحبت کردیم، مدتی می گذرد، هرچند که فقط کمی بیش از یک هفته گذشته است. از زمان آخرین مقاله ام، من به طور کامل در توسعه RSM غرق شده ام. امروز، من هیجان زده هستم که انتشار RSM نسخه 0.3.0 را به همراه اسناد پایه API اعلام کنم. می‌خواهم از همه تشویق‌هایی که از شما دریافت کردم، قدردانی کنم، چه تعاملات در رسانه‌های اجتماعی یا ستاره‌های موجود در مخزن RSM در GitHub. حمایت شما انگیزه بزرگی برای من بوده است، متشکرم! (به خصوص ستاره ها، آنها واقعاً من را هیجان زده می کنند.)

اکنون، بیایید به ویژگی‌های خاصی که RSM ارائه می‌کند بپردازیم.

داده های کاملاً قابل تنظیم

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

آداپتور

هنگام استفاده از RSM، من به توسعه دهندگان توصیه می کنم یک آداپتور برای مدل ها ایجاد کنند. این آداپتور مقادیر حالت اولیه و عملیات مختلف برای حالت مانند خواندن و نوشتن را ارائه می دهد. ایجاد یک آداپتور می تواند دست و پا گیر باشد، بنابراین RSM در حال حاضر شامل پشتیبانی از یک آداپتور صفحه بندی است که از رویکرد شرکت ما الهام گرفته شده است. این آداپتور باید برای اکثر سناریوهای صفحه بندی کافی باشد.

از آنجایی که هدف اولیه من آدرس دادن به صفحه بندی بود، RSM در حال حاضر فقط از آداپتور صفحه بندی پشتیبانی می کند. با این حال، در آینده، آداپتورهای اضافی برای برآوردن سایر نیازها اضافه خواهند شد. من همچنین از هر ایده ای که ممکن است داشته باشید استقبال می کنم.

کپی برداری

در Redux، deduplication نیز قابل دستیابی است. در شرکت من، ما کدی مانند این می نویسیم:

export const listAchievementBadge = createAsyncThunk<
  { items: BasicAchievementBadge[] },
  ListAchievementBadgePayload
>(
  `${ACHIEVEMENT_BADGE}/list`,
  (ctx, { lang }) => {
    const res = await listAchievementBadgeApi({lang});
    return {items: res.items}
  },
  {
    modifiers: [throttle(10000, { keySelector: (_, { lang }) => lang })],
  }
);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

throttle(10000) به این معنی است که همان اقدام در عرض 10 ثانیه ارسال نمی شود، و keySelector برای تعیین یکسان بودن عملکردها استفاده می شود. معمولاً اکثر APIها نیاز به حذف مجدد دارند، بنابراین اضافه کردن throttle هر اقدام جدید می تواند بسیار دست و پا گیر باشد. علاوه بر این، مهم است که تعریف کردن را فراموش نکنید keySelector، در غیر این صورت، هر ارسال اکشن منحصر به فرد تلقی می شود. یه بار یادم رفت اضافه کنم keySelector و پس از انتشار محصول با تماس‌های بیش از حد API مواجه شد.

RSM به طور خودکار کسر تکرار را کنترل می کند، بنابراین لازم نیست نگران ارسال مداوم درخواست های مکرر باشید.

اعتبار مجدد در فوکوس/اتصال مجدد

درست مثل React Query refetchOnWindowFocus و refetchOnReconnectRSM همچنین این ویژگی ها را برای بهبود تجربه کاربر فراهم می کند.

شرکت من این ویژگی را با Redux پیاده‌سازی نکرده است، اما فکر می‌کنم بسیار مشکل‌ساز خواهد بود.

نظرسنجی

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

خطا سعی مجدد

هنگامی که هنگام واکشی یک API با خطا مواجه می شوید، می توانید به سادگی یک خطا ایجاد کنید و RSM به طور خودکار دوباره تلاش می کند تا زمانی که تعداد تلاش های تعیین شده قبل از پرتاب خطا به دست آید. البته می توانید این ویژگی را نیز غیرفعال کنید تا یک شکست API به عنوان یک خطا در نظر گرفته شود و به کاربر نشان داده شود.

داده ها را باطل کنید

در حال حاضر، RSM فقط از باطل کردن یک تکه داده پشتیبانی می‌کند و مانند React Query نمی‌تواند چندین ورودی داده را همزمان باطل کند. به عنوان مثال، اگر کلیدهای پرس و جو مانند زیر دارید:

['posts', 'list', 'all']
['posts', 'get', 1]
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

استفاده كردن queryClient.invalidateQueries({queryKey: ['post']}) تمام داده های ذکر شده را به عنوان قدیمی علامت گذاری می کند. این یک ویژگی عالی است و انتظار می رود RSM در آینده از عملکردهای مشابه پشتیبانی کند.

در TypeScript نوشته شده است

امروزه استفاده از TypeScript به یک روش استاندارد برای بسته ها تبدیل شده است. نوشتن کد با TypeScript تجربه توسعه دلپذیرتری را فراهم می کند.

شروع شدن

پس از بحث در مورد همه اینها، من معتقدم که بسیاری از شما ممکن است هنوز در مورد نحوه استفاده از RSM مطمئن نباشید (زیرا کد مرتبطی در بالا ذکر نشده است). اکنون، بیایید به نحوه استفاده واقعی از RSM بپردازیم!

ایجاد مدل

برخلاف Redux که داده ها در فروشگاه متمرکز هستند، RSM از توسعه دهندگان می خواهد که داده ها را دسته بندی کرده و مدل های مختلفی را برای انواع داده های مختلف ایجاد کنند. به عنوان مثال، در شرکت ما انواع داده مانند پست ها، نظرات و انجمن ها داریم، بنابراین باید مدل های جداگانه ای برای آنها ایجاد کنیم. دلیل این امر این است که انواع داده های مختلف ممکن است به ساختارهای داده متفاوتی نیاز داشته باشند. به عنوان مثال، پست ها ممکن است برای ساختار داده صفحه بندی مناسب باشند، اما تنظیمات کاربر ممکن است مناسب نباشند.

import { createPaginationAdapter, createModel } from 'react-server-model';

const postAdapter = createPaginationAdapter<Post>();
const postModel = createModel(postAdapter.initialState);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اکسسوری با useAccessor قلاب

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

const getPostById = postModel.defineAccessor<number, Post>('normal', {
    fetchData: async (id) => {
        const data = await getPostApi(id);
        return data;
    },
    syncState: (draft, payload) => {
        postAdapter.upsertOne(draft, payload.data);
    }
})
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اولین استدلال از defineAccessor روش فقط می پذیرد 'normal' یا 'infinite'. معمولاً فقط باید استفاده کنید 'infinite' هنگام اجرای بارگذاری بی نهایت در بیشتر موارد با استفاده از 'normal' کافی است.

آرگومان دوم مربوط به Accessor’s است عمل. fetchData به کاربر می گوید که چگونه داده ها را از سرور واکشی کند، و syncState نحوه همگام سازی داده های دریافتی با وضعیت مدل را مشخص می کند.

این defineAccessor متد یک تابع ایجاد کننده دسترسی را برمی گرداند. اگر همان آرگومان ها را پاس کنید، همان Accessor را برمی گرداند. حالا بیایید از Accessor ایجاد شده توسط استفاده کنیم defineAccessor با useAccessor قلاب.

function usePost(id: number) {
    const accessor = getPostById(id);
    const { data, error, isFetching } = useAccessor(accessor, state => postAdapter.tryReadOne(state, id));

    return { post: data, error, isFetching, revalidate: () => accessor.revalidate() };
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

استدلال دوم از useAccessor شکل را تعیین می کند data. می توانید آن را به عنوان یک تابع انتخابگر در Redux در نظر بگیرید. در RSM این پارامتر را صدا می زنیم getSnapshot زیرا تصویری از وضعیت مدل ارائه می دهد. اگر فقط می خواهید عنوان یک پست خاص را بازیابی کنید، می توانید آن را به صورت زیر بنویسید:

function usePostTitle(id: number) {
    const accessor = getPostById(id);
    const { data } = useAccessor(accessor, state => postAdapter.tryReadOne(state, id)?.title);

    return data;
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اگرچه هر دو قلاب مشترک یک Accessor هستند، اما به دلیل تفاوت در آرگومان دوم، در زمان‌های متفاوتی رندر می‌شوند. usePost زمانی که داده‌های پست مربوطه برای داده‌های داده شده، دوباره رندر می‌شوند id تغییر می کند، در حالی که usePostTitle تنها زمانی بازپرداخت می شود که عنوان پست مربوطه با داده شده باشد id تغییر می کند.

توجه به این نکته ضروری است getSnapshot به اکسسوری گره خورده است. باید اطمینان حاصل کنید که لوازم و وضعیت استفاده شده در getSnapshot پارامترهای لازم برای ایجاد کننده دسترسی هستند. در غیر این صورت ممکن است با رفتار غیرمنتظره ای مواجه شوید. در مثال بالا فقط id دسترسی را تحت تأثیر قرار می دهد، بنابراین فقط id را می توان در آن گنجاند getSnapshot. شما می توانید این محدودیت را شبیه به آرایه وابستگی ها در نظر بگیرید useEffect.

علاوه بر استفاده با useAccessor، خود اکسسوری ها روش هایی دارند که می توان از آنها استفاده کرد مانند accessor.revalidate استفاده شده در usePost. اگر فرآیند اعتبارسنجی مجدد برای دسترسی وجود نداشته باشد، فراخوانی این روش داده‌ها را واکشی می‌کند و آن را با مدل همگام‌سازی می‌کند.

مزایای داده های سفارشی شده

اکنون، بیایید بررسی کنیم که چگونه RSM به مسائل ذکر شده در مقاله قبلی من رسیدگی می کند.

خوانندگانی که آشنایی ندارند می توانند به این مقاله مراجعه کنند.

ابتدا بیایید تعریف کنیم getPostList.

const getPostList = postModel.defineAccessor<string, Post>('infinite', {
    fetchData: async (filter, { previousData }) => {
        if (previousData.length === 0) return null;
        const data = await getPostListApi(filter);
        return data;
    },
    syncState: (draft, payload) => {
        postAdapter.appendPagination(draft, payload.filter, payload.data);
    }
})
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

getPostList لیست پست های مختلف را بر اساس موارد مختلف واکشی می کند filter این لیست ها را در حالت مدل ارزش گذاری و ذخیره می کند.

فرض کنید صفحه‌بندی‌ها را به‌صورت جداگانه واکشی کردیم filter ارزش های 'latest' و 'popular'و هر دو لیست حاوی یک پست با شناسه 100 هستند. حالا اگر کاربری روی این پست نظر بگذارد و کد زیر را اجرا کنیم:

postModel.mutate(draft => {
    postAdapter.readOne(draft, 100).totalCommentCount += 1;
})
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

هنگامی که کاربر به لیست محبوب تغییر می کند، می بیند که تعداد کل نظرات پست با شناسه 100 به روز شده است. اگر سپس به آخرین لیست تغییر مکان دهند، همان نتیجه را نیز خواهند دید. همه اینها به لطف ساختار داده سفارشی شده در RSM امکان پذیر است. در آداپتور صفحه‌بندی، همه نهادها به صورت مرکزی مدیریت می‌شوند و صفحه‌بندی اطلاعات را بر اساس شناسه‌ها ادعا می‌کند. بنابراین، هر زمان که یک موجودیت به روز شود، هر صفحه بندی که شامل آن موجودیت باشد نیز به روز می شود. این کاملاً مشکلی را که قبلاً ذکر شد حل می کند.

نتیجه

اگرچه RSM هنوز یک بسته بالغ نیست، من به بهبود آن ادامه خواهم داد. علاوه بر این، من موفق شده ام اعضای ارشد شرکت خود را متقاعد کنم که آن را در محصولات ما اعمال کنند. امیدوارم بتواند به ما در حذف کامل Redux کمک کند.

در پایان می خواهم یک بار دیگر از جامعه تشکر کنم. بدون تشویق و حمایت عملی شما، اعضای ارشد شرکت ما ممکن است با اجازه من این کار را نپذیرند. به لطف شناخت شما از RSM است که من توانستم از این فرصت استفاده کنم. بسیار از شما متشکرم! همچنین امیدوارم RSM بتواند به خوانندگانی که به دنبال راه حل های جایگزین هستند کمک کند. روز فوق العاده ای داشته باشید!

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا