К содержимому
Hub
Все заметки

24 августа 2026 г. · 3 мин чтения

Кнопка нажимается и не делает ничего

Next.jsReactОтладкаИнтерфейс

На сайте перестала работать переписка. Симптом был один и очень неприятный: человек нажимает «Написать», и не происходит вообще ничего. Ни перехода, ни сообщения об ошибке, ни значка загрузки. Кнопка как будто не нажимается.

Такая поломка хуже честного «не получилось». Про ошибку с текстом понятно, что делать: перечитать, попробовать иначе, написать в поддержку. Про молчание непонятно ничего — человек решает, что сайт сломан, и уходит.

Причин оказалось три, и они накладывались друг на друга.

Первая. Серверное действие может не вернуть отказ, а бросить исключение. Чаще всего это происходит, когда истекла сессия: страж requireUser не отвечает «нельзя», он именно бросает. В обработчике нажатия без try/catch это исключение теряется, и функция просто не доходит до строки, которая что-то показывает.

Вторая. Действие отработало, а переход не случился. router.push, вызванный сразу после серверного действия, у нас молча не срабатывал: в базе разговор появлялся, а страница оставалась прежней. Разбираться в тонкостях того, почему именно, оказалось дороже, чем заменить переход на обычный window.location.assign — он делает ровно то, что обещает, ценой полной перезагрузки, которой в этом месте никто не заметит.

Третья, самая обидная. Страница, на которую вёл переход, падала с серверной ошибкой — о ней отдельная заметка. То есть даже если бы первые две работали, человек попал бы на 500.

Вывод, который стоит унести: в интерфейсе не должно быть состояния «ничего не произошло». Любое нажатие обязано закончиться одним из трёх — получилось, не получилось и вот почему, или идёт загрузка. Если код может выйти из обработчика, не показав ничего, рано или поздно он так и сделает.

React · TSX
function write(userId: string) {  start(async () => {    try {      const result = await openConversation(userId);      if (result.ok && result.conversationId) {        // Обычный переход, а не router.push: тот, вызванный сразу        // после серверного действия, молча не срабатывал.        window.location.assign(          `/${locale}/account/messages/${result.conversationId}`,        );      } else {        setSearchNote(result.message ?? "Не удалось открыть переписку");      }    } catch {      // Действие может БРОСИТЬ, а не вернуть отказ — например, когда      // истекла сессия. Без перехвата кнопка молча не делала бы ничего.      setSearchNote(        "Не получилось — возможно, истёк вход. Обновите страницу и войдите заново.",      );    }  });}