Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Як захистити форму від подвійної відправки й гонитви запитів у React?

Подвійний клік, Enter у полі під час повільного запиту, повторна відправка після «зависання» - типові джерела дублікатів замовлень і коментарів. Захист потрібен на кількох рівнях.

1. Інтерфейс - не дати відправити вдруге:

function SubmitButton() {
  const { pending } = useFormStatus();
  return <button disabled={pending}>{pending ? 'Оформлюємо...' : 'Оформити'}</button>;
}

З useActionState дії ставляться в чергу й виконуються по черзі, а не паралельно, - результат передбачуваний навіть при повторних натисканнях. Але вони все одно виконаються всі.

2. Своя логіка відправки - прапорець у ref, а не лише стан:

const submitting = useRef(false);

async function handleSubmit(values) {
  if (submitting.current) return;
  submitting.current = true;
  try {
    await api.placeOrder(values);
  } finally {
    submitting.current = false;
  }
}

Стан (useState) оновлюється з рендером - два швидкі кліки можуть обидва побачити false. Ref змінюється одразу.

3. Гонитва відповідей - коли новий запит починається до завершення попереднього (пошук, фільтри, автозбереження). Стара відповідь може прийти пізніше й перезаписати нову:

useEffect(() => {
  const controller = new AbortController();
  fetch(`/api/search?q=${encodeURIComponent(query)}`, { signal: controller.signal })
    .then((r) => r.json())
    .then(setResults)
    .catch((e) => { if (e.name !== 'AbortError') throw e; });
  return () => controller.abort();   // скасувати попередній запит
}, [query]);

Або бібліотеки даних (TanStack Query), що розв'язують це самі.

4. Сервер - останній і головний рубіж. Клієнтський захист обходиться повторним запитом, повільною мережею з повтором, двома вкладками:

  • ключ ідемпотентності: клієнт генерує crypto.randomUUID() при відкритті форми й надсилає його з запитом; сервер не створює вдруге замовлення з тим самим ключем;
  • унікальні обмеження в базі (одна підписка на email);
  • блокування (Cache::lock у Laravel) для операцій, що не повинні виконуватися паралельно.

5. Після успіху - перенаправлення чи скидання форми, щоб «Назад» і повторне натискання не відправили ті самі дані знову.

Докладніше в документації: form: стан очікування

Схожі питання