0

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

6
  • here is working demo on class component : stackblitz.com/edit/react-fystht Commented Feb 27, 2022 at 9:04
  • Basically you just copied class component code, there is no second argument in hooks API, such code is not valid: setPosts(res.data.slice(0, 20), handleScrollPosition());, use useEffect instead. Try searching a bit for similar questions on migrating from class setState to hooks. Commented Feb 27, 2022 at 9:21
  • @DennisVash, oh, man, while I was typing answer you've already given correct hint here. Go ahead and make official answer Commented Feb 27, 2022 at 9:22
  • You can write an answer I think there should be a duplicate question no time to look for. Commented Feb 27, 2022 at 9:23
  • there are few different issues in original code so it's hard to find out one specific thread to link this to Commented Feb 27, 2022 at 9:24

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

5 Comments

Also its good to notice that session storage is useless here, he might want a simple state or a ref
yep, but since "list" component is dropped after navigation to "Details" I think, it's easier to make a route "list with ID of element we want scroll to" instead of introducing state in some root component to keep "element under focus"
thanks for the details) and how could I implement it with IDs? Actually, on my real project items have ID. Now I navigate with history.push('/') but as you know it directs without restoring scroll position.
@MukhriddinShakhriyorov I don't have short and single answer to this. It depends on how your routes are organized now and what's the version of React Router do you use(e.g. for React Router 6 there is useSearchParams). Better to ask in separate question.
@skyboyer I tried to explain here. Pls, have a look taking time

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.