恢复反应项目中的滚动位置(钩子)

发布于 2025-01-10 07:29:30 字数 190 浏览 1 评论 0原文

我搜索了一些有关我的问题的答案,但发现了一个相关的答案。不幸的是,它甚至在类组件上,所以我想在导航回功能组件后恢复滚动位置。在这里我将分享 Stackblitz 上的源代码链接

I have searched a few answers regarding my issue but found one relevant. Unfortunately, it is even on the class component, so I want to restore the scroll position after navigating back on the functional component. Here I will share the source code link on Stackblitz

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

濫情▎り 2025-01-17 07:29:30

这里有几个问题。

第一:

setPosts(res.data.slice(0, 20), handleScrollPosition());

你看,useState 中的 setter 没有 this.setState() 那样有第二个参数。但是,您能问一下,为什么您的 handleScrollPosition 会被调用吗?(因为您可以看到它在调试器中被调用)。原因是您已将其编写为 handleScrollPosition(),因此会立即调用它。为了证明我的观点,如果您将其更改为正确的“传递回调”形式:

setPosts(res.data.slice(0, 20), handleScrollPosition);

正如您在基于类的版本中已经拥有的那样,您将看到它从未被调用。同样,

[].map(() => {}, 1,2,3,4, handleScrollPosition())

即使显然 [].map() 不处理第 6 个参数,也会调用 handleScrollPosition()

那么你应该做什么?首先,让我们将滚动恢复移动到单独的 useEffect 中:

useEffect(() => {
    const scrollPosition = sessionStorage.getItem("scrollPosition");
    if (scrollPosition) {
      window.scrollTo(0, parseInt(scrollPosition));
      sessionStorage.removeItem("scrollPosition");
    }
}, []);

显然它效果不佳,因为它将在获取数据之前立即调用。因此,让我们添加对 posts 的依赖:

useEffect(() => {...
}, [posts]);

但它仍然无法正常工作。原因是它在 posts 为空时在初始渲染时触发...所以让我们添加检查“仅在加载帖子后”:

useEffect(() => {
  if (posts.length) {
    const scrollPosition = sessionStorage.getItem("scrollPosition");
    if (scrollPosition) {
      window.scrollTo(0, parseInt(scrollPosition));
      sessionStorage.removeItem("scrollPosition");
    }
  }
}, [posts]);

现在它按预期工作并恢复滚动位置。

PS 顺便说一句,我不认为将位置写入 sessionStorage 是最好的方法。使用多个选项卡会造成混乱。如果没有示例代码,我会通过创建单独的路线“也是列表,但带有我们应该滚动到的元素的 ID”来看到替代方案。然后链接“返回列表”将定位该路线,并传递您当前正在查看详细信息的实体 ID。

There are few issues here.

First:

setPosts(res.data.slice(0, 20), handleScrollPosition());

You see, setter in useState does not have second argument as this.setState() has. But why, can you ask, your handleScrollPosition is called at all?(since you can see it's called in debugger). The reason is that you've written this as handleScrollPosition() so it's called immediately. To demonstrate my point, if you change it to correct "passing a callback" form:

setPosts(res.data.slice(0, 20), handleScrollPosition);

As you already have in class-based version, you will see it's never called. Similarly,

[].map(() => {}, 1,2,3,4, handleScrollPosition())

will also call handleScrollPosition() even though obviously [].map() does not process 6th argument.

So what you should do? First, let's move scroll restoring into separate useEffect:

useEffect(() => {
    const scrollPosition = sessionStorage.getItem("scrollPosition");
    if (scrollPosition) {
      window.scrollTo(0, parseInt(scrollPosition));
      sessionStorage.removeItem("scrollPosition");
    }
}, []);

obviously it did not work well, since it will be called immediately, before data is fetched. So let's add dependency on posts:

useEffect(() => {...
}, [posts]);

But it will not work correctly, still. The reason is it's triggered on initial render when posts is empty... so let's add check "only after posts are loaded":

useEffect(() => {
  if (posts.length) {
    const scrollPosition = sessionStorage.getItem("scrollPosition");
    if (scrollPosition) {
      window.scrollTo(0, parseInt(scrollPosition));
      sessionStorage.removeItem("scrollPosition");
    }
  }
}, [posts]);

Now it works as expected and restores scroll position.

PS btw, I don't think that writting position to sessionStorage is the best approach. Working with multiple tabs will make a mess. If speaking without sample code, I'd see alternative by making separate route "also the list but with ID of element we should scroll to". And then link "back to the list" will target that route, with passing of entity ID you are currently viewing details for.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文