vercel / next.js

The React Framework
https://nextjs.org
MIT License
127.09k stars 27.01k forks source link

Docs: How to migrate from async router.push to new `next/navigation` push method #61737

Open ajwootto opened 9 months ago

ajwootto commented 9 months ago

What is the improvement or update you wish to see?

The new router in next/navigation no longer returns a promise from the push method. From what I've been able to understand, this is so that it can work with the startTransition feature of React, but the Next docs don't contain any information about this or how to successfully replace any functionality that was relying on "awaiting" a router.push call

Is there any context that might help us understand?

The docs for the push method are currently one sentence here: https://nextjs.org/docs/app/api-reference/functions/use-router#userouter

The migration guide does not mention the fact that the method is no longer async or describe how a migration should take place to the new push https://nextjs.org/docs/app/building-your-application/upgrading/app-router-migration#step-5-migrating-routing-hooks

This is the one place I've been able to find mention of how it can be used with useTransition, although it also doesn't seem to trigger the pending state in many cases (maybe it just doesn't work in page router?) https://github.com/vercel/next.js/discussions/49810

Does the docs page already exist? Please link to it.

https://nextjs.org/docs/app/building-your-application/upgrading/app-router-migration#step-5-migrating-routing-hooks

Guo-dalu commented 9 months ago

Here here, also need it badly.

arcticfly commented 5 months ago

Agreed. We built our own hacky solution to await router pushes, but would really like built-in support for this!

ArianHamdi commented 5 months ago

Agreed. We built our own hacky solution to await router pushes, but would really like built-in support for this!

Can you please share your workaround

arcticfly commented 5 months ago

Agreed. We built our own hacky solution to await router pushes, but would really like built-in support for this!

Can you please share your workaround


// modify router so that it awaits the route change before returning
export const useAsyncRouter = () => {
const router = useRouter();
const pathname = usePathname();
const [resolveRouteChanged, setResolveRouteChanged] = useState<(() => void) | null>(null);

useEffect(() => { resolveRouteChanged?.(); }, [pathname]);

const asyncRouter = useMemo(() => { const push = async (href: NextRouteTypes.RouteImpl) => { router.push(href as any);

  await new Promise<void>((resolve) => {
    // wait for the route change to complete
    setResolveRouteChanged(resolve);
  });
};

return { ...router, push };

}, [router]);

return asyncRouter; };

Cauen commented 3 months ago

Here are an alternative implementation of https://stackoverflow.com/a/77931487/6638583 workaround code

'use client'
import { useRouter } from 'next/navigation'
import { useEffect, useTransition } from 'react'

// Define the type for the observer callback function
type ObserverCallback = () => void

const createRouteObserver = () => {
  let observer: ObserverCallback | null = null

  const setObserver = (callback: ObserverCallback) => {
    observer = callback
  }

  const notify = () => {
    if (observer) {
      observer()
    }
  }

  return { setObserver, notify }
}

const routeObserver = createRouteObserver()

export const useAsyncRoutePush = () => {
  const [isPending, startTransition] = useTransition()
  const router = useRouter()

  const asynPush = async (path: string) => {
    return new Promise<void>((resolve) => {
      startTransition(() => {
        router.push(path)
      })

      routeObserver.setObserver(() => {
        resolve()
      })
    })
  }

  useEffect(() => {
    if (!isPending) {
      routeObserver.notify()
    }
  }, [isPending])

  return asynPush
}

Usage:

export default function MyComponent() {
  const asyncPush = useAsyncRoutePush()

  return <Button onClick={async () => {
    await asyncPush('/')
    console.log("Route changed")
  }}>
    Go / route
  </Button>
}
allicanseenow commented 2 months ago

This should be mentioned in the documentation as a breaking change as there's no built-in support for this feature now.

TradeeHubDev commented 5 days ago

This feature is really needed because I have an MUI grid which I added custom url tracking of the page in the url via push and when user clicks so fast since it's async all of it that means the router.push is not being awaited and clicking too fast is causing a crash look below

image