我遇到了 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 就运行一次,仅此而已。有什么想法吗?
请小心您放入钩子中的依赖项。您不应该输入
router
或 changeActivity
。在您的示例中,calloutSet
来自何处并不明显。但是当窗口位置改变时,你的 useEffect()
函数必须被调用,这是肯定的。所以我的建议是将 window.location
放在 useEffect()
的依赖项中。还要注意,没有对依赖关系进行深入比较,因此,如果通过引用传递变量,则确保它们的引用每次都会更改。有时我必须使用 JSON.stringify()
来传递对象的完整副本,这样如果任何属性发生更改,就会有效地调用钩子。