Next.js 按下后退按钮时的行为
Next.js behavior on back button pressed
我有一个页面正在尝试修复,以便在用户按下后退按钮(浏览器)时保持滚动位置。假设我有一个名为列表的组件,我在其中向用户展示了一些产品。要查看所有产品,用户可以向下滚动列表组件。当用户单击某些产品时,应用程序会将用户重定向到详细信息组件。然后当用户试图返回列表时,点击浏览器的后退按钮,列表组件被渲染并且它似乎自动滚动到顶部。
据我所知,按下浏览器的后退按钮会触发一个 window.history.back()
动作,没有其他任何反应。
对于一个解决方案,我在我的应用程序上下文中实现了一个变量,它保存了 scrollY 值,然后在我尝试的组件的 componentWillMount
(或 useEffect
)中render(列表组件),我将滚动位置设置为上下文中设置的值。
我的解决方案的详细信息在这里,因为我的整个代码都基于此堆栈溢出 post:
我已经使用一些日志检查了值并且滚动位置在上下文中正确保存,但是,因为我使用的是 window 事件侦听器,它在列表组件之后将值设置为零已渲染。
在我的代码中,我没有使用任何类型的滚动配置,所以我想知道这种行为是否是 Next.js 或 React 的某种默认行为。当用户点击浏览器的后退按钮时会发生这种情况,但我是 next 的新手,我不知道我是否遗漏了什么或者什么,我什至不知道这个问题是否与 React 或Next.js 本身。
查看您的 url,使用浅路由可以解决问题。 URL 将在何处更新。并且页面不会被替换,只会更改路由的状态。所以你可以根据那个改变你的逻辑。
官方文档中有一个很好的例子:
https://nextjs.org/docs/routing/shallow-routing
并且您可以使用 display: 'hidden' 来根据您的状态有条件地隐藏和显示您的组件!
这是一种解决方法,但根据您的具体情况,它可能更有用!
在寻找另一个不使用 window.scroll 和类似方法的解决方案后,我找到了解决方案。
第一个解决方案(有效,但对我来说,我有一个通过 API 调用加载的无限列表,有时 window.scroll 方法不准确):我采用 window.scrollY 值并在会话存储中设置它,我在离开列表页面之前这样做了,所以在详细信息页面中,如果用户点击后退按钮,在页面加载的那一刻,我从会话存储中获取 Y 值并使用 window.scroll 方法强制页面滚动到先前配置的值。
正如我之前提到的,这有效,但在我的例子中,我有一个从异步 API 调用填充的列表,所以有时加载的页面没有所有图像并且滚动已经配置,然后图像和数据已加载,用户最终看到了页面中的其他位置,而不是期望的位置。
第二个解决方案:在我的例子中,我们正在谈论一个电子商务应用程序,所以我发现这个解决方案很有用,因为它专注于具有相应 ID 的特定项目,而不是 window 的 Y 坐标。 Scroll Restoration in e commerce app
这个要点可能会有所帮助,因为它包含一个自定义挂钩来管理滚动位置:https://gist.github.com/claus/992a5596d6532ac91b24abe24e10ae81
import { useEffect } from 'react';
import Router from 'next/router';
function saveScrollPos(url) {
const scrollPos = { x: window.scrollX, y: window.scrollY };
sessionStorage.setItem(url, JSON.stringify(scrollPos));
}
function restoreScrollPos(url) {
const scrollPos = JSON.parse(sessionStorage.getItem(url));
if (scrollPos) {
window.scrollTo(scrollPos.x, scrollPos.y);
}
}
export default function useScrollRestoration(router) {
useEffect(() => {
if ('scrollRestoration' in window.history) {
let shouldScrollRestore = false;
window.history.scrollRestoration = 'manual';
restoreScrollPos(router.asPath);
const onBeforeUnload = event => {
saveScrollPos(router.asPath);
delete event['returnValue'];
};
const onRouteChangeStart = () => {
saveScrollPos(router.asPath);
};
const onRouteChangeComplete = url => {
if (shouldScrollRestore) {
shouldScrollRestore = false;
restoreScrollPos(url);
}
};
window.addEventListener('beforeunload', onBeforeUnload);
Router.events.on('routeChangeStart', onRouteChangeStart);
Router.events.on('routeChangeComplete', onRouteChangeComplete);
Router.beforePopState(() => {
shouldScrollRestore = true;
return true;
});
return () => {
window.removeEventListener('beforeunload', onBeforeUnload);
Router.events.off('routeChangeStart', onRouteChangeStart);
Router.events.off('routeChangeComplete', onRouteChangeComplete);
Router.beforePopState(() => true);
};
}
}, [router]);
}
我有一个页面正在尝试修复,以便在用户按下后退按钮(浏览器)时保持滚动位置。假设我有一个名为列表的组件,我在其中向用户展示了一些产品。要查看所有产品,用户可以向下滚动列表组件。当用户单击某些产品时,应用程序会将用户重定向到详细信息组件。然后当用户试图返回列表时,点击浏览器的后退按钮,列表组件被渲染并且它似乎自动滚动到顶部。
据我所知,按下浏览器的后退按钮会触发一个 window.history.back()
动作,没有其他任何反应。
对于一个解决方案,我在我的应用程序上下文中实现了一个变量,它保存了 scrollY 值,然后在我尝试的组件的 componentWillMount
(或 useEffect
)中render(列表组件),我将滚动位置设置为上下文中设置的值。
我的解决方案的详细信息在这里,因为我的整个代码都基于此堆栈溢出 post:
我已经使用一些日志检查了值并且滚动位置在上下文中正确保存,但是,因为我使用的是 window 事件侦听器,它在列表组件之后将值设置为零已渲染。
在我的代码中,我没有使用任何类型的滚动配置,所以我想知道这种行为是否是 Next.js 或 React 的某种默认行为。当用户点击浏览器的后退按钮时会发生这种情况,但我是 next 的新手,我不知道我是否遗漏了什么或者什么,我什至不知道这个问题是否与 React 或Next.js 本身。
查看您的 url,使用浅路由可以解决问题。 URL 将在何处更新。并且页面不会被替换,只会更改路由的状态。所以你可以根据那个改变你的逻辑。
官方文档中有一个很好的例子: https://nextjs.org/docs/routing/shallow-routing
并且您可以使用 display: 'hidden' 来根据您的状态有条件地隐藏和显示您的组件!
这是一种解决方法,但根据您的具体情况,它可能更有用!
在寻找另一个不使用 window.scroll 和类似方法的解决方案后,我找到了解决方案。
第一个解决方案(有效,但对我来说,我有一个通过 API 调用加载的无限列表,有时 window.scroll 方法不准确):我采用 window.scrollY 值并在会话存储中设置它,我在离开列表页面之前这样做了,所以在详细信息页面中,如果用户点击后退按钮,在页面加载的那一刻,我从会话存储中获取 Y 值并使用 window.scroll 方法强制页面滚动到先前配置的值。 正如我之前提到的,这有效,但在我的例子中,我有一个从异步 API 调用填充的列表,所以有时加载的页面没有所有图像并且滚动已经配置,然后图像和数据已加载,用户最终看到了页面中的其他位置,而不是期望的位置。
第二个解决方案:在我的例子中,我们正在谈论一个电子商务应用程序,所以我发现这个解决方案很有用,因为它专注于具有相应 ID 的特定项目,而不是 window 的 Y 坐标。 Scroll Restoration in e commerce app
这个要点可能会有所帮助,因为它包含一个自定义挂钩来管理滚动位置:https://gist.github.com/claus/992a5596d6532ac91b24abe24e10ae81
import { useEffect } from 'react';
import Router from 'next/router';
function saveScrollPos(url) {
const scrollPos = { x: window.scrollX, y: window.scrollY };
sessionStorage.setItem(url, JSON.stringify(scrollPos));
}
function restoreScrollPos(url) {
const scrollPos = JSON.parse(sessionStorage.getItem(url));
if (scrollPos) {
window.scrollTo(scrollPos.x, scrollPos.y);
}
}
export default function useScrollRestoration(router) {
useEffect(() => {
if ('scrollRestoration' in window.history) {
let shouldScrollRestore = false;
window.history.scrollRestoration = 'manual';
restoreScrollPos(router.asPath);
const onBeforeUnload = event => {
saveScrollPos(router.asPath);
delete event['returnValue'];
};
const onRouteChangeStart = () => {
saveScrollPos(router.asPath);
};
const onRouteChangeComplete = url => {
if (shouldScrollRestore) {
shouldScrollRestore = false;
restoreScrollPos(url);
}
};
window.addEventListener('beforeunload', onBeforeUnload);
Router.events.on('routeChangeStart', onRouteChangeStart);
Router.events.on('routeChangeComplete', onRouteChangeComplete);
Router.beforePopState(() => {
shouldScrollRestore = true;
return true;
});
return () => {
window.removeEventListener('beforeunload', onBeforeUnload);
Router.events.off('routeChangeStart', onRouteChangeStart);
Router.events.off('routeChangeComplete', onRouteChangeComplete);
Router.beforePopState(() => true);
};
}
}, [router]);
}