جهش های ساده با TanStack Query و Next.js

جهش با TanStack Query همیشه کمی ترسناک بوده است – حداقل تا زمانی که تصمیم گرفتم آنها را امتحان کنم. پس از استفاده زیاد از آنها دیگر راه برگشتی وجود ندارد.
بنابراین امروز میخواهم به شما نشان دهم که چگونه میتوانید اولین جهش خود را بنویسید و شما را از طریق مشکلات کوچکی که ممکن است در این راه رخ دهد راهنمایی کنید.
کد این مثال در این مخزن است که حاوی مجموعه ای از مفاهیمی که من نوشتم، اثبات TanStack است. جهشی که ما در مورد آن صحبت می کنیم اینجاست.
جهش برای چیست؟
جهشها طوری طراحی شدهاند که برای هر چیزی که عملیات GET نیست استفاده شود، بنابراین میتوانیم از آنها برای ارسال، قرار دادن و/یا حذف دادهها استفاده کنیم. برای ساده کردن این کار، من یک پایگاه داده ساختگی با json-server و چند todo-item راه اندازی کردم که شخصیت های محبوب ما از دنیای Rick and Morty باید در طول قسمت ها انجام دهند. اجرا را فراموش نکنید npm run json-server
برای راه اندازی آن
ما می خواهیم نتیجه چیزی شبیه به این باشد:
یک کار جدید ایجاد کنید
بیایید ابتدا با نوشتن تابع برای POST واقعی شروع کنیم. این باید خیلی سرراست باشد.
async function createTodo(todo) {
const response = await fetch("http://localhost:8000/todos", {
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({
id: nanoid(),
user: todo.user,
task: todo.task,
done: false,
}),
});
const result = await response.json();
if (!response.ok) {
throw new Error(result.message);
}
return result;
}
این فقط از fetch برای ارسال درخواست پست با TODO به عنوان آرگومان استفاده می کند و نتیجه را برمی گرداند. اگر مشکلی پیش بیاید، خطا رخ می دهد.
اکنون میتوانیم فرمی ایجاد کنیم تا کارهای خود را در آن بنویسیم:
<form onSubmit={onFormSubmit} className="grid grid-cols-1 gap-4">
<div className="flex gap-4 items-end">
<label htmlFor="user" className="text-xl font-bold">
User:
</label>
<input
type="text"
className="text-black flex-1"
name="user"
id="user"
onChange={changeTodo}
value={todo.user}
/>
</div>
<div className="flex gap-4 items-end">
<label htmlFor="task" className="text-xl font-bold">
Task:
</label>
<input
type="text"
className="text-black flex-1"
name="task"
id="task"
onChange={changeTodo}
value={todo.task}
/>
</div>
<button type="submit" className="justify-self-end">
create todo
</button>
</form>
البته، این به برخی حالت ها و توابع نیز نیاز دارد که تغییر حالت را مدیریت می کند:
const [todo, setTodo] = useState({ user: "", task: "" });
function changeTodo(e) {
const newObject = {};
newObject[e.target.name] = e.target.value;
setTodo({ ...todo, ...newObject });
}
اکنون که این تنظیم شده است، بیایید به خود جهش نگاه کنیم. قلاب useMutation تنها یک آرگومان اجباری دارد، که تابعی است که به API ما ارسال میکند و باید یک Promise را برگرداند. در مورد ما، این خواهد بود:
const createTodoMutation = useMutation(createTodo)
اکنون، تنها کاری که باید انجام دهید این است که هر بار که یک todo ارسال میشود، به فرم بگویید که دادهها را جهش کند. برای مثال می توانید این کار را در کنترل کننده onSubmit انجام دهید:
function onFormSubmit(e) {
e.preventDefault();
createTodoMutation.mutate(todo);
}
پس بیایید خلاصه کنیم:
- کاربر یک todo را در فیلد تایپ می کند و روی ارسال کلیک می کند.
- در کنترل کننده ارسال، از پیش فرض جلوگیری می شود و تابع mutate برای قلاب useMutation فراخوانی می شود. به عنوان یک آرگومان، داده هایی را که می خواهید به تابع createTodo منتقل کنید، می گیرد.
- تابع createTodo تماس API را انجام می دهد و اکنون باید ببینید که سرور json درخواست POST را در گزارش های خود نشان می دهد. همچنین، کار جدید شما باید در داخل فایل db.json قابل مشاهده باشد.
لیست همه کارها
همه اینها خوب و خوب است، اما ما هنوز نمی توانیم کارهای خود را در صفحه ببینیم، فرم فقط به ما اجازه می دهد موارد جدید را ارسال کنیم. پس بیایید چند کد سریع برای تجسم کارهایمان اضافه کنیم.
ما به یک تابع برای واکشی از API خود نیاز داریم:
async function fetchTodos() {
return fetch("http://localhost:8000/todos").then((res) => res.json());
}
در کامپوننت خود، می توانیم از قلاب useQuery برای به دست آوردن کارهای خود استفاده کنیم:
const todos = useQuery(["todos"], fetchTodos);
برای اینکه کد خود را پاکتر کنم، یک جزء todo اضافی ایجاد کردم:
function Todo({ todo }) {
return (
<div className="bg-white text-black rounded-lg grid grid-cols-[auto_1fr_auto] items-center gap-4 py-2 px-4">
<input
type="checkbox"
checked={todo.done}
onChange={(e) => changeTodoStatus(e, todo.id)}
/>
<p>{todo.task}</p>
<Image
src={todo.user}
alt="profile picture"
className="rounded-full"
height={60}
with={60}
></Image>
</div>
);
}
و اکنون فقط باید کارهای خود را تکرار کنیم و آنها را به این صورت نشان دهیم:
{todos.isLoading ? <span className="loader"></span> : null}
<div className="grid grid-cols-2 gap-4 pt-8">
{todos.isSuccess
? todos.data.map((todo) => <Todo todo={todo} key={todo.id}></Todo>)
: null}
</div>
کار ما تمام شد، درست است؟
اگر این را امتحان کردید، انتظار دارید که کار جدید پس از ایجاد آن به طور خودکار ظاهر شود، اما این اتفاق نمیافتد. دلیلش این است که ما هرگز به قلاب useQuery خود نگفتیم که کوئری خود را به روز کند. از آنجایی که پایگاه داده تغییر کرده است، ما باید راهی برای ارتباط قلاب ها با یکدیگر پیدا کنیم: آیا قلاب useMutation نمی تواند به قلاب useQuery بگوید که باید کش را به روز کند؟
بله می تواند و این کاری است که ما می خواهیم انجام دهیم. بیایید استفاده قبلی خود را برای موارد زیر تغییر دهیم:
const queryClient = useQueryClient();
const changeTodoMutation = useMutation(updateTodo, {
onSuccess: () => queryClient.invalidateQueries({ queryKeys: ["todos"] }),
});
ما تصمیم داریم پرس و جوهایی را که حاوی queryKey “todos” هستند، بی اعتبار کنیم، که روشی است برای درخواست از TanStack query برای به روز رسانی داده های مربوطه. همچنین میتوانیم کاری مانند «queryClient.refetch({queryKeys: [“todos”]})، که ضروری است، اما راه توصیه شده فقط این است که حافظه پنهان را باطل کنید و به کتابخانه اجازه دهید بقیه را مدیریت کند.
نتیجه
این یک مثال بسیار ابتدایی است که من زیاد استفاده کرده ام: پست کردن چیزی و بازیابی فوری آن برای به روز نگه داشتن رابط کاربری. شاید قبلاً مشکوک باشید که چگونه یک کار واحد را به روز کنید؟ خودتان آن را امتحان کنید و سپس راه حل موجود در مخزن را بررسی کنید.
_
عکس کارلوس موزا در Unsplash