TL;DR
Использование Optimistic UI в React позволяет мгновенно обновлять интерфейс, создавая иллюзию быстрого отклика приложения. В статье рассмотрим, как реализовать эту технику с помощью TanStack Query (бывший React Query) для улучшения пользовательского опыта.
Введение: контекст и актуальность
В традиционной веб-разработке пользовательский интерфейс часто блокируется на время выполнения запросов к серверу, что делает приложение медленным и неудобным. Optimistic UI — это подход, при котором интерфейс мгновенно обновляется, а запрос выполняется в фоновом режиме. Если запрос завершится ошибкой, интерфейс возвращается к предыдущему состоянию. Этот подход особенно полезен для микро-взаимодействий, таких как отметка задачи как выполненной или лайк комментария.
Основная часть с примерами кода
Проблема с Loading Spinners
Традиционный подход с использованием спиннеров заставляет пользователя ждать завершения запроса, что снижает воспринимаемую производительность приложения. Optimistic UI позволяет избежать этого, мгновенно обновляя интерфейс.
Реализация Optimistic UI с TanStack Query
Рассмотрим пример реализации Optimistic UI для отметки задачи как выполненной в приложении управления проектами.
// components/TaskItem.tsx
import { useMutation, useQueryClient } from '@tanstack/react-query';
import axios from 'axios';
export default function TaskItem({ task }) {
const queryClient = useQueryClient();
const mutation = useMutation({
// 1. Фактический API вызов
mutationFn: (newStatus: boolean) => {
return axios.patch(`/api/tasks/${task.id}`, { completed: newStatus });
},
// 2. Оптимистичное обновление (выполняется сразу при вызове mutate())
onMutate: async (newStatus) => {
// Отменяем текущие запросы, чтобы они не перезаписали оптимистичное обновление
await queryClient.cancelQueries({ queryKey: ['tasks'] });
// Сохраняем предыдущее состояние для возможного отката
const previousTasks = queryClient.getQueryData(['tasks']);
// Оптимистично обновляем кэш мгновенно
queryClient.setQueryData(['tasks'], (oldData: any) => {
return oldData.map((t: any) =>
t.id === task.id ? { ...t, completed: newStatus } : t
);
});
// Возвращаем объект контекста с сохраненным предыдущим состоянием
return { previousTasks };
},
// 3. Если запрос завершился ошибкой, используем контекст для отката
onError: (err, newStatus, context) => {
queryClient.setQueryData(['tasks'], context?.previousTasks);
alert("Не удалось обновить задачу. Пожалуйста, попробуйте снова.");
},
// 4. Всегда обновляем данные после успешного или неудачного запроса для синхронизации с сервером
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['tasks'] });
},
});
return (
<div className="flex items-center gap-3">
<input
type="checkbox"
checked={task.completed}
onChange={(e) => mutation.mutate(e.target.checked)}
/>
<span className={task.completed ? "line-through" : ""}>
{task.title}
</span>
</div>
);
}
Влияние на воспринимаемую производительность
Реализация Optimistic UI делает приложение более отзывчивым и приятным в использовании. Пользователи могут быстро взаимодействовать с интерфейсом без необходимости ждать завершения запросов.
Практическое применение
Optimistic UI особенно полезен в приложениях с интенсивным взаимодействием пользователя, таких как управление задачами, социальные сети или платформы электронной коммерции. Использование TanStack Query упрощает реализацию этой техники, делая код более читаемым и поддерживаемым.
Заключение
Optimistic UI — это мощный подход для улучшения пользовательского опыта, который позволяет мгновенно обновлять интерфейс и маскировать задержки сервера. Использование TanStack Query делает реализацию этой техники простой и эффективной, что позволяет создавать более отзывчивые и приятные в использовании приложения.
Источник: https://dev.to/iprajapatiparesh/stop-using-loading-spinners-master-optimistic-ui-in-react-4m9c