Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Состояние ожидания и оптимистичный интерфейс

Пока сервер выполняет loader или action, пользователь должен видеть, что что-то происходит, иначе он нажмёт кнопку ещё раз. Хук useNavigation сообщает, идёт ли сейчас переход или отправка формы. Оптимистичный интерфейс идёт дальше: показывает результат сразу, не дожидаясь ответа.

Состояния навигации

useNavigation() возвращает объект, главное поле которого — state:

stateЧто происходит
idleНичего, страница готова
submittingФорма отправлена, выполняется action
loadingЗагружаются данные новой страницы

Кнопка во время отправки

// app/routes/new-post.tsx
import { Form, useNavigation } from "react-router";
 
export default function NewPost() {
  const navigation = useNavigation();
  const isSubmitting = navigation.state === "submitting";
 
  return (
    <Form method="post">
      <input name="title" placeholder="Заголовок" />
      <textarea name="body" placeholder="Текст статьи" />
      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? "Публикую..." : "Опубликовать"}
      </button>
    </Form>
  );
}

После нажатия кнопка станет неактивной с текстом «Публикую...», а после ответа сервера откроется страница статьи. На быстром компьютере это мгновение; замедлите сеть во вкладке Network инструментов разработчика, чтобы увидеть надпись.

Индикатор загрузки страниц

Общий индикатор удобно поместить в макет app/routes/layout.tsx:

// app/routes/layout.tsx
import { Outlet, useNavigation } from "react-router";
 
export default function Layout() {
  const navigation = useNavigation();
  const isLoading = navigation.state !== "idle";
 
  return (
    <>
      <header>Мой блог {isLoading && <span>Загрузка...</span>}</header>
      <main style={{ opacity: isLoading ? 0.5 : 1 }}>
        <Outlet />
      </main>
    </>
  );
}

При каждом переходе в шапке появится «Загрузка...», а старая страница станет полупрозрачной, пока не придут новые данные.

Оптимистичный интерфейс

Во время отправки в navigation.formData лежат отправленные поля. Их можно показать сразу:

// app/routes/new-post.tsx
const title = navigation.formData?.get("title");
 
{title && <p>Публикуется: {String(title)}</p>}

Пользователь видит заголовок новой статьи ещё до ответа сервера. Если сервер вернёт ошибку, formData исчезнет, и вместо этой строки отрисуются ошибки проверки.

Частая ошибка

Проверять navigation.state === "submitting" в макете, чтобы показать «Отправка формы» только на нужной странице. Состояние общее для всего приложения: оно будет submitting при отправке любой формы. Чтобы отличить свою форму, сравнивайте ещё и navigation.formAction с адресом, на который она отправляется, — или используйте useFetcher из следующего урока, у которого состояние своё.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.