Skip to content

Unexpected behaviour when redirecting in action #2997

@liegeandlief

Description

@liegeandlief

What version of Remix are you using?

1.4.1

Steps to Reproduce

Create a Remix project (with the built in Remix server) with the following routes:

index.tsx:

import { memo } from 'react'

const IndexRoute = memo(() => {
  return <h1>Home</h1>
})
IndexRoute.displayName = 'IndexRoute'

export default IndexRoute

magic-link.tsx:

import { memo, useEffect } from 'react'
import { LoaderFunction, json, useLoaderData, useSubmit } from 'remix'
import { emailFormFieldName, codeFormFieldName } from '~/routes/verify-magic-link'

type LoaderData = {
  email: string,
  code: string
}

const loader: LoaderFunction = async ({ request }) => {
  const url = new URL(request.url)
  const email = url.searchParams.get("email") ?? ''
  const code = url.searchParams.get("code") ?? ''

  return json<LoaderData>({
    email,
    code
  })
}

const MagicLinkRoute = memo(() => {
  const loaderData = useLoaderData<LoaderData>()
  const submit = useSubmit()

  useEffect(() => {
    const formData = new FormData()
    formData.append(emailFormFieldName, loaderData.email)
    formData.append(codeFormFieldName, loaderData.code)
    submit(formData, { method: 'post', action: '/verify-magic-link' })
  }, [loaderData.email, loaderData.code, submit])

  return (
    <p>Some pending UI</p>
  )
})
MagicLinkRoute.displayName = 'MagicLinkRoute'

export default MagicLinkRoute
export { loader }

verify-magic-link.tsx:

import { memo } from 'react'
import { ActionFunction, json, redirect, useActionData } from 'remix'

export const emailFormFieldName = 'email'
export const codeFormFieldName = 'code'

// fake verifyMagicLink function with forced failure
const verifyMagicLink = ({ email, code } : { email: string, code: string  }) => Promise.resolve(false)

type ActionData = Awaited<ReturnType<typeof verifyMagicLink>>

const action: ActionFunction = async ({ request }) => {
  const body = await request.formData()
  const email = body.get(emailFormFieldName)?.toString() ?? ''
  const code = body.get(codeFormFieldName)?.toString() ?? ''

  const verifyMagicLinkResult = await verifyMagicLink({ email, code })

  if (verifyMagicLinkResult === true) {
    return redirect('/')
  }

  return json<ActionData>(verifyMagicLinkResult)
}

const VerifyMagicLinkRoute = memo(() => {
  const actionData = useActionData<ActionData>()

  return (
    <p>{JSON.stringify(actionData, null, 2)}</p>
  )
})
VerifyMagicLinkRoute.displayName = 'VerifyMagicLinkRoute'

export default VerifyMagicLinkRoute
export { action }

In the entry.server.tsx handleRequest function put the following just before returning the response:

  console.log({
    type: 'handleRequest',
    url: request.url,
    method: request.method,
    response: newResponse.status
  })

In the entry.server.tsx handleDataRequest function put the following just before returning the response:

  console.log({
    type: 'handleDataRequest',
    url: request.url,
    method: request.method,
    response: newResponse.status
  })

Go to https://localhost:3000/magic-link?code=123456&[email protected] in your browser. In the terminal you should see:

{
  type: 'handleRequest',
  url: 'http://localhost:3000/magic-link?code=123456&[email protected]',
  method: 'GET',
  response: 200
}

{
  type: 'handleDataRequest',
  url: 'http://localhost:3000/verify-magic-link?_data=routes%2Fverify-magic-link',
  method: 'POST',
  response: 200
}

{
  type: 'handleDataRequest',
  url: 'http://localhost:3000/verify-magic-link?_data=root',
  method: 'GET',
  response: 200
}

This is what I would expect to see.

Now in verify-magic-link.tsx change const verifyMagicLink = ({ email, code } : { email: string, code: string }) => Promise.resolve(false) to const verifyMagicLink = ({ email, code } : { email: string, code: string }) => Promise.resolve(true).

Run the same request in your browser again.

Expected Behavior

Terminal should show:

{
  type: 'handleRequest',
  url: 'http://localhost:3000/magic-link?code=123456&[email protected]',
  method: 'GET',
  response: 200
}

{
  type: 'handleDataRequest',
  url: 'http://localhost:3000/verify-magic-link?_data=routes%2Fverify-magic-link',
  method: 'POST',
  response: 302
}

{
  type: 'handleDataRequest',
  url: 'http://localhost:3000/?_data=root',
  method: 'GET',
  response: 200
}

Actual Behavior

Terminal shows:

{
  type: 'handleRequest',
  url: 'http://localhost:3000/magic-link?code=123456&[email protected]',
  method: 'GET',
  response: 200
}

{
  type: 'handleDataRequest',
  url: 'http://localhost:3000/?_data=root',
  method: 'GET',
  response: 200
}

The POST request does not get logged by either handleRequest or handleDataRequest.

Even though I am taken to the index.tsx route as expected the 302 redirect response never seems to be returned to the browser. Instead when I look in the browser network tab I can see that the the response to the POST request was a 204 response.

This is causing me issues because I would like to add a set-cookie header on the redirect response but the cookie never gets set because the redirect response doesn't make it to the browser.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions