تصور کنید روی پروژهای کار میکنید که در آن باید دادهها را از یک API واکشی کنید، برای عملکرد بهتر آنها را در حافظه پنهان ذخیره کنید و در پسزمینه همگامسازی کنید. همچنین ممکن است لازم باشد صفحه بندی، بارگذاری بی نهایت و دیگر سناریوهای پیچیده را مدیریت کنید. انجام تمام این کارها به صورت دستی میتواند کاری دلهرهآور باشد و منجر به کدهای دیگ بخار زیادی شود.
React Query یک کتابخانه قدرتمند است که توسط TanStack توسعه یافته است که واکشی داده ها و مدیریت استیت را در برنامه های React ساده می کند. این یک راه ساده برای مدیریت داده های راه دور و همگام نگه داشتن آن با رابط کاربری ارائه می دهد. این مقاله شما را با React Query، ویژگی های کلیدی آن و نحوه شروع استفاده از آن در پروژه های React خود آشنا می کند.
React Query چیست؟
React Query یک کتابخانه جاوا اسکریپت است که برای ساده کردن کار پیچیده واکشی و ذخیره اطلاعات در برنامه های React طراحی شده است. مجموعهای از هوک ها و ابزارهای کمکی را ارائه میکند که به شما امکان میدهد دادهها را از منابع مختلف، از جمله REST API، GraphQL یا حتی استیت محلی، بدون زحمت مدیریت کنید.
React Query یک کتابخانه مدیریت استیت نیست، اما اغلب در کنار کتابخانههای مدیریت استیت مانند Redux یا Zustand برای مدیریت وضعیت سرور، که استیتی است که از درخواستهای ناهمزمان به دست میآید، استفاده میشود.
برای آشنایی با نحوه استفاده GraphQL در React می توانید این مقاله را بررسی کنید.
ویژگی های کلیدی React Query
واکشی داده های اعلامی: React Query یک رویکرد اعلامی برای واکشی داده ها را ترویج می کند. شما کوئری ها و جهش ها را با استفاده از هوک هایی مانند useQuery و useMutation تعریف می کنید. این منجر به کد تمیزتر و سازماندهی شده تر می شود.
ذخیره خودکار: React Query شامل یک کش داخلی است که نتایج کوئری را ذخیره می کند. هنگامی که جهش رخ می دهد، به طور خودکار داده ها را به روز می کند و اطمینان حاصل می کند که رابط کاربری شما ثابت می ماند.
همگامسازی دادههای پسزمینه: میتواند بهطور خودکار دادهها را در پسزمینه بازیابی کند و دادههای شما را بدون دخالت دستی تازه نگه دارد.
صفحهبندی و پیمایش بینهایت: React Query ابزارهایی را برای مدیریت صفحهبندی و پیمایش بینهایت بدون دردسر فراهم میکند.
بهروزرسانیهای خوشبینانه: میتوانید بهراحتی بهروزرسانیهای خوشبینانه را پیادهسازی کنید، و باعث میشود برنامهتان پاسخگوتر شود.
چرا باید React Query استفاده کنیم؟
یکی از چالشهایی که هنگام ساخت برنامههای React با آن مواجه هستیم، تعیین یک الگوی مؤثر برای (واکشی و بهروزرسانی) کار با وضعیت سرور است. React هیچ چیزی را از جعبه به ما نمی دهد.
در نتیجه، توسعهدهندگان راههای خود را با واکشی دادهها (وضعیت سرور) در داخل یک هوک useEffect
ایجاد میکنند، سپس نتیجه را در یک حالت مبتنی بر کامپوننت (استیت کلاینت) کپی میکنند. این الگو کار می کند اما بهینه نیست.
بیایید نقاط منفی این الگو را با در نظر گرفتن کد زیر نشان دهیم:
import "./styles.css";
import React, {useEffect, useState} from "react";
import axios from 'axios';
export default function App() {
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
const [userData, setUserData] = useState(null);
useEffect(() => {
async function getUserData() {
try {
setIsLoading(true);
const {data} = await axios.get(`https://api.anophel.com/api/v1/users/1`);
setUserData(data);
setIsLoading(false);
} catch (error) {
setIsLoading(false);
setIsError(error);
}
}
getUserData();
}, []);
return (
<div>
{isLoading && (<div> ...Loading </div>)}
{isError && (<div>An error occured: {isError.message}</div>)}
{userData && (<div>The username is : {userData.username}</div>)}
</div>
)
}
در مثال کوچک ساخته شده در بالا، ما دادههای کاربر را از نقطه پایانی 1/https://api.anophel.com/api/v1/users
واکشی میکنیم. سپس یک view base را در مورد وضعیت (موفقیت، بارگیری یا خطا) فراخوانی API ارائه می کنیم. این روش چندین مشکل دارد از جمله:
ما باید این تماس را در کامپوننت App یا هر کامپوننت ای در بالای درخت کامپوننت خود انجام دهیم. این برای فعال کردن انتقال داده ها به اجزای دیگر (تودرتو) است که با استفاده از prop drilling به آنها نیاز دارند.prop drilling در React یک ضد الگو است و باید به هر طریقی از آن اجتناب کرد. در اینجا چند استراتژی برای کمک به جلوگیری از prop drilling آورده شده است
ما باید این کد دیگ بخار را در هر کامپوننت ای که داده واکشی می کنیم تکرار کنیم. مثال بالا هم به useState
و هم به هوک useEffect نیاز دارد و ما از سه حالت مختلف کلاینت (محلی) (isLoading، isError و userData) برای تعیین وضعیت فراخوانی API استفاده کردیم. همه اینها باید در هر کامپوننت ای که برای واکشی داده نیاز داریم بازنویسی شود.
اگرچه میتوانیم منطق هوکهای این کد دیگ بخار را در یک هوک قابل استفاده مجدد سفارشی انتزاع کنیم و از آن در سراسر برنامه خود استفاده مجدد کنیم، اما هنوز همه مشکلات ما را حل نمیکند.
با این الگو، توسعه دهندگان اغلب در دام مخلوط کردن حالت کلاینت و سرور با هم می افتند. شیء حالت حاصل ممکن است شامل برخی از حالت های مبتنی بر کامپوننت (کلاینت) باشد، مانند وضعیت نوار کناری یا رنگ طرح زمینه با وضعیت سرور، به عنوان مثال داده های کاربر واکشی شده. شیء حالت نهایی ممکن است چیزی شبیه به این باشد:
const [state, setState] = {
showSideBar: false,
theme: "dark",
currentUser: {},
users: [],
posts: []
};
برای جلوگیری از این مشکل، برخی از توسعه دهندگان به کتابخانه های مدیریت استیت گلوبال مانند Mobx و Redux روی می آورند. اگرچه این ممکن است کارساز باشد، آنها یک لایه پیچیدگی اضافی به برنامه ما اضافه می کنند و در برخی موارد ممکن است بیش از حد باشد.
همچنین، در حالی که برخی از کتابخانه های مدیریت استیت سنتی در مدیریت وضعیت کلاینت عالی هستند، در مدیریت وضعیت سرور چندان کارآمد نیستند.
مدیریت وضعیت سرور به دلیل موارد زیر الزامات منحصر به فردی برای این امر دارد:
از راه دور ادامه دارد و در کنترل ما نیست
توسط افراد دیگر قابل دسترسی و تغییر است
ممکن است کهنه شود
برای واکشی و بهروزرسانی به یک API ناهمزمان نیاز دارد
در نتیجه، برای مدیریت موثر وضعیت سرور به موارد زیر نیاز داریم:
برای ذخیره وضعیت سرور ما در یک حافظه پنهان
مکانیزمی برای دانستن اینکه آیا وضعیت تغییر کرده است یا خیر
برای به روز رسانی دوره ای وضعیت در پس زمینه
برای منعکس کردن بهروزرسانیها در سریعترین زمان ممکن
اینها ویژگی هایی نیستند که به راحتی بتوانیم آن ها را به تنهایی کدنویسی کنیم. خوشبختانه، اینها مشکلاتی است که React Query برای حل آنها ایجاد شده است.
خارج از جعبه، React Query مجموعهای از هوکها را برای واکشی، ذخیرهسازی حافظه پنهان و بهروزرسانی دادههای همگام (وضعیت سرور) به ما میدهد.
چگونه از React Query استفاده خواهیم کرد
هنگامی که از React Query برای واکشی داده ها استفاده می کنید، نتایج در یک کش محلی ذخیره می شوند. این بدان معنی است که اگر دوباره همان داده را درخواست کنید، React Query به جای برقراری تماس API دیگر، نتایج ذخیره شده را برمی گرداند. هنگامی که داده ها تغییر می کنند، حافظه پنهان به طور خودکار باطل می شود، بنابراین همیشه آخرین داده ها را دریافت خواهید کرد.
React Query علاوه بر کش کردن، از بازیابی پسزمینه نیز پشتیبانی میکند. این بدان معنی است که اگر دادههای ذخیرهشده در حافظه پنهان قدیمی باشند، React Query به طور خودکار دادههای جدیدی را در پسزمینه دریافت میکند. زمان بیات مدت زمانی است که دادههای ذخیرهشده قبل از واکشی مجدد اجازه دارند قدیمی شوند. زمان بیات پیش فرض 5 دقیقه است.
ترکیبی از کش محلی و واکشی مجدد پسزمینه، React Query را به ابزاری قدرتمند برای مدیریت واکشی دادهها در برنامههای React تبدیل میکند. می تواند به شما در بهبود عملکرد، کاهش تماس های API و اطمینان از به روز بودن داده های شما کمک کند.
برای آشنایی با Redux Toolkit این مقاله را بررسی کنید.
شروع کار با React Query
بیایید به یک مثال اساسی بپردازیم تا ببینیم شروع با React Query چقدر ساده است. اولین کاری که باید انجام دهید این است که tanstack/react-query@
را نصب کنید. من از npm برای اجرا استفاده خواهم کرد، شما می توانید از yarn یا bun نیز استفاده کنید. برای نصب React نیز می توانید از Vite.js استفاده کنید.
npm install @tanstack/react-query
پس از نصب کتابخانه در برنامه ما، یک ارائه دهنده و کلاینت ایجاد کنید تا از React Query استفاده کنید. می توانید آن را در index.tsx
در پوشه src
ایجاد کنید.
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { QueryClientProvider, QueryClient } from '@tanstack/react-query';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
const queryClient = new QueryClient();
root.render
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
);
پس از آن، می توانید بلافاصله از React Query Hooks استفاده کنید. بیایید آن را در App.tsx
اعمال کنیم.
import React from 'react';
import logo from './logo.svg';
import './App.css';
import { useQuery } from '@tanstack/react-query';
function App() {
const userData = useQuery(
['users'],
() => {
return fetch('https://api.anophel.com/api/v1/users').then(response => response.json());
},
{
enabled: false,
}
);
return (
<div>
<div>
<button onClick={() => userData.refetch()}>Get Users</button>
<div>
{userData.isFetching && (
<div>Fetching user data...</div>
)}
{userData.isError && (
<div>{`Error get data!!!`}</div>
)}
{userData.data && userData.data.length > 0 && userData.data.map((user: any) => (
<div>{user.name}</div>
))}
</div>
</div>
</div>
);
}
export default App;
در این مثال، ما از useQuery
برای واکشی لیستی از کاربران استفاده می کنیم. React Query وضعیت واکشی، مدیریت خطا و کش کردن در پشت صحنه را کنترل می کند.
ما در اینجا از سرویس API آنوفل که به صورت رایگان است استفاده کردیم و شما می توانید برای تست اپلیکیشن های خود بدون نیاز به دانش بک اند و پرداخت هزینه ای از آن استفاده کنید. برای دسترسی به این سرویس نیز می توانید وارد پنل کاربری شوید و از API های آنوفل همراه با مثال استفاده کنید.
وقتی روی دکمه Get Users
کلیک می کنیم، داده ها را از API دریافت می کنیم. از آنجایی که مقدار داده کم است، فرآیند واکشی به دلیل سرعت بسیار زیاد کمتر قابل مشاهده است.
به غیر از useQuery
، ما useMutation
را نیز داریم که این هوک ها تقریباً مشابه useQuery
هستند، اما برای جهش داده ها استفاده می شوند. در زیر نمونه ای از کاربرد آن را نشان می دهم. فقط این کد را اضافه کنید.
import React from 'react';
import logo from './logo.svg';
import './App.css';
import { useQuery, useMutation } from '@tanstack/react-query';
function App() {
const userData = useQuery(
['users'],
() => {
return fetch('https://api.anophel.com/api/v1/users').then(response => response.json());
},
{
enabled: false,
}
);
const mutatePost = useMutation(
['posts'],
(newPost: any) => {
return fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
body: JSON.stringify(newPost),
headers: {
'Content-type': 'application/json; charset=UTF-8',
},
}).then((response) => response.json())
}
)
return (
<div>
<div>
<button onClick={() => userData.refetch()}>Get Users</button>
<div>
{userData.isFetching && (
<div>Fetching user data...</div>
)}
{userData.isError && (
<div>{`Error get data!!!`}</div>
)}
{userData.data && userData.data.length > 0 && userData.data.map((user: any) => (
<div>{user.name}</div>
))}
</div>
</div>
<hr />
<div>
<button onClick={() => mutatePost.mutate({ title: 'First Post', body: 'First Post Body', userId: 1 })}>Add New Post</button>
<div>
{mutatePost.isLoading && (
<div>Adding new post...</div>
)}
{mutatePost.isError && (
<div>{`Error add new post!!!`}</div>
)}
{mutatePost.data && (
<div>{`Success add new post with title : '${mutatePost.data.title}'`}</div>
)}
</div>
</div>
</div>
);
}
export default App;
در مثال بالا از useMutation
برای اضافه کردن یک پست جدید استفاده می کنیم.
تفاوت بین useQuery و useMutation
هدف: useQuery
برای خواندن داده ها است، در حالی که useMutation
برای اصلاح داده ها است.
مورد استفاده معمولی: useQuery
زمانی استفاده میشود که میخواهید دادهها را واکشی و نمایش دهید، در حالی که useMutation
زمانی استفاده میشود که میخواهید تغییراتی در آن دادهها ایجاد کنید.
مقادیر بازگشتی: useQuery { data, error, isLoading, isFetching }
را برمی گرداند در حالی که useMutation { mutate, data, error, isError, isLoading
, isSuccess }
را برمی گرداند.
Error Handling: هر دو هوک خطاها را کنترل می کنند، اما useMutation
ویژگی های اضافی را برای مدیریت به روز رسانی های خوش بینانه و بازگشت در صورت بروز خطا در حین جهش فراهم می کند.
نتیجه
React Query افزودنی ارزشمند به اکوسیستم React است که واکشی و همگامسازی دادهها را آسانتر از همیشه میکند. چه در حال ساخت یک برنامه کوچک یا یک پروژه در مقیاس بزرگ باشید، سادگی و ویژگی های قدرتمند React Query به شما کمک می کند تا داده های خود را به طور موثر مدیریت کنید.
با سادهسازی مدیریت دادهها، React Query به شما این امکان را میدهد تا بر ساخت ویژگیهای برنامه خود و ارائه تجربه کاربری بهتر تمرکز کنید. آن را در پروژه بعدی React خود امتحان کنید و احتمالاً تعجب خواهید کرد که چگونه بدون آن داده ها را مدیریت کرده اید.