Next.js Discord

Discord Forum

Nextjs router causing useEffect to re-render

Answered
Carpenter ant posted this in #help-forum
Open in Discord
Carpenter antOP
Hi, I'm encountering an issue with Next.js's next/navigation router. I have a set of useEffects in my component, and for some reason calling router.replace causes one of the effects to run a second a time, and in some cases just run infinitely. This causes a set of elements to play its transition animation twice (or just get stuck running infinitely) every time the user clicks a button that changes the activity.
Answered by Ray
because router.replace() will navigate to the page but not adding new entry to the browser history. so the useEffect run again when the page load.
try using window.history.replaceState() instead if you want to update the url without navigation
window.history.replaceState(null, '', `${window.location.pathname}?${urlParams.toString()}`)

https://nextjs.org/docs/app/building-your-application/routing/linking-and-navigating#windowhistoryreplacestate
View full answer

13 Replies

Carpenter antOP
const [selectedActivity, setSelectedActivity] = useState<Activity | null | undefined>(undefined)
const [customNames, setCustomNames] = useState<CustomNames>({})
const router = useRouter()
...
const changeActivity = useCallback((activity: Activity | null) => {
    setSelectedActivity(activity)

    const urlParams = new URLSearchParams(window.location.search);
    if (activity) {
        urlParams.set('activity', activity.id)
    }
    else {
        urlParams.delete('activity')
    }

    // set query params to reflect the selected activity, or remove them if null
    router.replace(`${window.location.pathname}?${urlParams.toString()}`)
}, [router])
useEffect(() => {
    if (calloutSet) {
        // read query params to see if activity is specified
        const urlParams = new URLSearchParams(window.location.search);
        const activityId = urlParams.get('activity');
        const activity = calloutSet.activities.find(activity => activity.id == activityId)

        changeActivity(activity ?? null)

        // also populate custom names
        const customNames: CustomNames = {}

        // Convert urlParams.entries() to an array and iterate over it
        Array.from(urlParams.entries()).forEach(([key, value]) => {
            const imageId = parseInt(key)
            if (isNaN(imageId)) return

            const imageReference = calloutSet.allImages.find(image => image.id == imageId)
            if (!imageReference) return

            // If the name is the same as the default, remove it from the custom names
            if (imageReference.name != value) {
                customNames[imageId] = value
            }
        })

        setCustomNames(customNames)

        // if the callout set is not custom, attempt to load the custom names from local storage
        if (!urlParams.has('isCustom') || urlParams.get('isCustom') != 'true') {
            const customNamesJson = localStorage.getItem(`${calloutSet.id}.customNames`)
            if (customNamesJson) {
                setCustomNames(JSON.parse(customNamesJson))
            }
        }
    }
}, [calloutSet, changeActivity])
By commenting out the router.replace line in changeActivity, the erroneous re-rendering stopped (I still want the URL to update though). Removing calloutSet and changeActivity from the dependency list also worked, but ESLint would start yelling at me and it causes issues with my navbar search bar. I just want the useEffect to run once whenever the activity changes, and that's it. Any ideas?
Wool sower gall maker
dont know which next js version u are running just found this
@Carpenter ant im using 14.0.1
Wool sower gall maker
maybe try to update and it fixes your problem
Carpenter antOP
alright lemme see
@Wool sower gall maker maybe try to update and it fixes your problem
Carpenter antOP
nope, issue still persists
Carpenter antOP
bump
Answer
@Carpenter ant THANK YOU 🙏
did it work for you?
Carpenter antOP
yup