-
Notifications
You must be signed in to change notification settings - Fork 27.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
next/router - push function changes between renders when called #18127
Comments
Hi @jamesmosier - I would be of the opinion that the various router methods should be using The React hooks lint rule will complain if you do not include all dependencies in the array - we would prefer to not have to disable this lint rule. |
I ran into a similar issue in my project. I created a custom hook to handle query parameters using next's router. It relied on a useEffect that had the router in the dependency array. It would be really helpful if the router's methods could use the useCallback hook so my custom hook could properly work. |
Same here — it feels like the |
just want to resurface this issue. it would be great for widely consumed callbacks like this to be memoized |
Be careful with this - passed the last 4 hours investigating why my cache stopped working and it was due to the fact that |
Router dependency warnings are the last things ESLint complains in my codebases. I agree with the suggestions that |
I don't know the internal workings of |
just run into the same problem with Problem is with nextjs 11, the default linting rules will force you to include Its good that nextjs 11 default to these rules, but it should therefore follow conventions to avoid pitfalls like this. Edit: I had to disable other solution is that nextjs declares exceptions in esint rule for |
As I see it, Next.js Router has to be split to router commends and route values. |
Be careful.
@jamesmosier this is unsafe in asynchronous scenarios, e.g. const router = useRouter()
// Safe
useEffect(() => {
if (!loggedIn) {
router.push('/login')
}
}, [loggedIn])
// Not safe (router.push could have changed)
useEffect(() => {
checkLoggedIn().then(loggedIn => {
if (!loggedIn) {
router.push('/login')
}
})
}, []) I quickly came up with this, which seems to have worked: import { useRouter } from 'next/router'
import type { NextRouter } from 'next/router'
import { useRef, useState } from 'react'
export default function usePush(): NextRouter['push'] {
const router = useRouter()
const routerRef = useRef(router)
routerRef.current = router
const [{ push }] = useState<Pick<NextRouter, 'push'>>({
push: path => routerRef.current.push(path),
})
return push
} It returns a const push = usePush()
useEffect(() => {
checkLoggedIn().then(loggedIn => {
if (!loggedIn) {
push('/login')
}
})
}, [push]) |
@steve-taylor would you mind explaining why this is not safe i.e. what's the effect of calling
|
I can suggest another workaround:
Some details: |
Hello everyone, are there any plans to fix this behavior? |
It's been over two years. This seems like an issue that will be encountered by anyone trying to sync state with url query params. so I'm surprised there isn't more activity here... Makes me think I'm doing url state syncing incorrectly or something... Currently using the fix mentioned above:
|
router as hooks dependency (since eslint rules mad about not including it) is not stable to use even in v13.x.x. |
Just lost a few hours on this bug. Why has this not been fixed yet? Using next@13.1.1 |
This is fixed in the new router that is part of the |
Thanks for the quick response @timneutkens. I look forward to migrating to the For the record, If the code is already available, I would think an opt-in via an explicit import would be the way to fix this without breaking existing apps, though I understand that this may not be worth the maintenance effort at this point. import { useRouter } from 'next/router-new-push' |
Unfortunately it's not the However, since we provide a backwards compat layer for Once that lands all you have to do is change to use |
) ## What? Ensures the router instance passed for `next/navigation` in Pages Router is a stable reference. For App Router the router instance is already a stable reference, so making this one stable too would fix #18127. ## How? Added `React.useMemo` around `adaptForAppRouterInstance`, previously it was recreated each render but that's not needed for this particular case. The searchParamsContext and pathnameContext do need a new value each render in order to ensure they get the latest value. Fixes #18127 Fixes NEXT-1375
) ## What? Ensures the router instance passed for `next/navigation` in Pages Router is a stable reference. For App Router the router instance is already a stable reference, so making this one stable too would fix #18127. ## How? Added `React.useMemo` around `adaptForAppRouterInstance`, previously it was recreated each render but that's not needed for this particular case. The searchParamsContext and pathnameContext do need a new value each render in order to ensure they get the latest value. Fixes #18127 Fixes NEXT-1375
This closed issue has been automatically locked because it had no new activity for a month. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you. |
Bug report
Depending on the
push
function ofuseRouter
causes an infinite loop when thepush
function is called from inside auseEffect
To Reproduce
[[...slug]].js
Example component
You will see an infinite loop of
called
in the consoleExpected behavior
Push should be a non-changing reference, so that the react diff algoirithm knows not to call side effects again uneccesarily
System information
NEXT-1375
The text was updated successfully, but these errors were encountered: