Действие формы / серверное действие вызывает повторный запрос fetch на странице SSR.

Вопрос или проблема

Я пытаюсь понять проблему, с которой мы сталкиваемся в нашем приложении Next.js версии 14 (route приложения).

У нас есть страница с серверным рендерингом (SSR), которая сначала проверяет JWT токен, вызывая конечную точку verify-token. Если токен действителен, мы рендерим форму (директива use-client) на этой странице. Форма использует действие на стороне клиента для проверки пользовательского ввода. После успешной проверки она инициирует действие на стороне сервера, которое взаимодействует с API для регистрации пользователя.

Проблема, которую мы наблюдаем, заключается в том, что после успешной отправки формы функция verifySignupToken вызывается снова. Поскольку мы выполняем навигацию на стороне клиента после отправки формы с помощью router.push, нам следует сразу перейти на новый маршрут? Я не уверен, почему она снова инициирует вызов fetch в родительском компоненте?

Страница SSR (с конфигурацией force-dynamic):

export const dynamic="force-dynamic"

export default async function Signup({ searchParams }: SignUpProps) {
  const { token, customerId, email } = searchParams

  const result = safeParse(VerifySignupSchema, { token, email, customerId })

  if (!result.success) {
    console.error(result.issues)
    redirect('/login?error=validation')
  }

  const validatedData = result.output

  try {
    await verifySignupToken(validatedData.token, validatedData.customerId, validatedData.email)
  } catch (error) {
    if (error instanceof Error) {
      redirect(`/login?error=${encodeURIComponent(error.message)}`)
    }
  }

  return (
    <SignUpForm
      token={validatedData.token}
      customerId={validatedData.customerId}
      email={validatedData.email}
    />
  )
}

SignUpForm (директива use-client с действием на стороне клиента, вызывающим действие на стороне сервера):

export const SignUpForm = ({ token, customerId, email }: SignUpFormProps) => {
  const router = useRouter()
  const { pending } = useFormStatus()

  const signUpClientAction = async (formData: FormData) => {
    const password = formData.get('password')
    const pnr = formData.get('pnr')

    const result = safeParse(SignUpSchema, { password, pnr })

    if (!result.success) {
      toast.error(result.issues[0].message)
      return
    }

    const validatedData = result.output

    const response = await signupAction(
      email,
      validatedData.password,
      token,
      validatedData.pnr,
      customerId
    )

    if (response.success) {
      toast.success(response.message)
      router.push("https://stackoverflow.com/")
    } else if (response.statusCode === 401) {
      toast.error(response.message)
      router.push('/login')
    } else {
      toast.error(response.message)
    }
  }

  return (
    <div className="mt-10">
      <div>
        <form className="space-y-6" action={signUpClientAction}>
    ....

.

Ответ или решение

Проблема, с которой вы столкнулись в вашем приложении на Next.js (версии 14 с использованием App Router), связана с тем, что после отправки формы происходит повторный вызов функции verifySignupToken, несмотря на использование клиентской навигации. Это может вводить в заблуждение, особенно если вы ожидаете, что после успешной отправки формы пользователь будет перенаправлен на новую страницу.

Почему происходит повторный вызов verifySignupToken?

  1. Серверный рендеринг (SSR): Поскольку страница рендерится на сервере, вызов verifySignupToken на стороне сервера осуществляется каждый раз при запросе к этой странице. С учетом того, что вы используете dynamic="force-dynamic", этот компонент будет обрабатываться как динамичный, но не гарантирует отсутствие повторного вызова при навигации. Сервер всегда будет обрабатывать запрос при каждой навигации на эту страницу, если это не будет явно указано.

  2. Перенаправление и повторный запрос: Когда ваш SignUpForm отправляет данные через клиентское действие signUpClientAction и, при успешном ответе, выполняется router.push, это скорее технически создает новый запрос к странице. Если рендеринг этой страницы требует валидации JWT, то вся логика в функции компонента будет заново вызвана, включая верификацию токена.

Как можно решить проблему?

Вот несколько рекомендаций, которые могут помочь избежать повторной валидации JWT после успешной отправки формы:

1. Перенаправление после отправки формы

Вместо того чтобы напрямую отправлять форму с помощью action={signUpClientAction}, вы можете использовать обработчик события onSubmit. Это позволит вам управлять процессом перенаправления более явно и избежать ненужного повторного вызова.

const handleSubmit = async (event) => {
  event.preventDefault(); // предотвращает отправку формы
  const result = await signUpClientAction(new FormData(event.target)); // отправка данных
  if (result.success) {
    router.push("https://stackoverflow.com/");
  }
};

return (
  <form className="space-y-6" onSubmit={handleSubmit}>
    {/* Ваши поля формы */}
  </form>
);

2. Использование состояния

Вы можете установить состояние в компоненте для отслеживания успешного результата отправки. Это состояние можно использовать для условного рендеринга, чтобы избегать вызова нежелательной логики после успешного выполнения.

const [isSubmitted, setIsSubmitted] = useState(false);

const signUpClientAction = async (formData) => {
  // Ваша логика отправки данных
  if (response.success) {
    setIsSubmitted(true); // отметка, что форма отправлена успешно
  }
};

if (isSubmitted) {
  return null; // или рендерьте другой компонент
}

3. Управление данными на стороне клиента

Перенос части логики в роутер или глобальное состояние (например, использование Redux или Context API) может помочь вам управлять состоянием и избежать вызова ненужных функций после перенаправления.

Заключение

Повторный вызов verifySignupToken связан с особенностями серверного рендеринга в Next.js и динамической маршрутизации. С помощью управления состоянием, обработчиков событий и перенаправления вы можете минимизировать повторные вызовы и улучшить пользовательский опыт.

Эти рекомендации помогут вам оптимизировать вашу архитектуру приложения и повысить его производительность. Если у вас возникнут дополнительные вопросы или необходимость в дальнейшей помощи, пожалуйста, не стесняйтесь обращаться.

Оцените материал
Добавить комментарий

Капча загружается...