Nextjs 路由器导致 useEffect 重新渲染

问题描述 投票:0回答:1

我遇到了 Next.js 的下一个/导航路由器的问题。我的组件中有一组

useEffect
,由于某种原因调用
router.replace
会导致其中一个效果一次运行一秒,在某些情况下只是无限运行。每次用户单击更改活动的按钮时,这都会导致一组元素播放其过渡动画两次(或者只是无限地卡住运行)。

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])

通过注释掉changeActivity中的

router.replace
行,错误的重新渲染停止了(不过我仍然希望更新URL)。从依赖项列表中删除
calloutSet
changeActivity
也有效,但 ESLint 会开始对我大喊大叫,这会导致我的导航栏搜索栏出现问题。我只希望每当活动发生变化时 useEffect 就运行一次,仅此而已。有什么想法吗?

reactjs typescript next.js react-hooks
1个回答
0
投票

请小心您放入钩子中的依赖项。您不应该输入

router
changeActivity
。在您的示例中,
calloutSet
来自何处并不明显。但是当窗口位置改变时,你的
useEffect()
函数必须被调用,这是肯定的。所以我的建议是将
window.location
放在
useEffect()
的依赖项中。还要注意,没有对依赖关系进行深入比较,因此,如果通过引用传递变量,则确保它们的引用每次都会更改。有时我必须使用
JSON.stringify()
来传递对象的完整副本,这样如果任何属性发生更改,就会有效地调用钩子。

© www.soinside.com 2019 - 2024. All rights reserved.