scroller

I introduced ScrollRestoration, but even if I refer to the official information, the scroll position is still at the top


<div>
        <OtherMain  >
         
          <Container>
          <ScrollRestoration />

            <Outlet />
            </Container>
                   
        </OtherMain>
         
        </div>

I created Root according to the official instructions.
Near <Outlet /> Placed <ScrollRestoration />
In createBrowserRouter, element: <AppLayout />, was placed in Child.
However, the scroll position is at the top and it does not start from the middle.

This may be unrelated, but the screen is divided into three vertically.
I am using version react-router-dom "^6.22.3",

"dependencies": { "@react-oauth/google": "^0.12.1", "@vitejs/plugin-legacy": "^5.3.2", "@vitejs/plugin-react-swc": "^3.6.0", "axios": "^1.6.8", "jwt-decode": "^4.0.0", "react": "^18.2.0", "react-chat-elements": "^12.0.14", "react-dom": "^18.2.0", "react-hot-toast": "^2.4.1", "react-icons": "^5.0.1", "react-query": "^3.39.3", "react-router-dom": "^6.22.3", "react-toastify": "^10.0.5", "react-use": "^17.5.0", "react-use-googlelogin": "^0.12.5", "stream-chat": "^8.26.0", "stream-chat-react": "^11.15.1", "styled-components": "^6.1.8", "vite-plugin-commonjs": "^0.10.1" },

App.jsx


function App() {

  
  
  const router = createBrowserRouter([
    {
      path: "/",
        element: <AppLayout />,
        children:[
          {
            path: "",
            element: <Navigate to="/login" />,
          },
          {
            path: "home",
            element: <Home />,
          },   
        ]
        
    }
    
  ]
  );
  return (
    <React.StrictMode>
              <RouterProvider router={router}>
              </RouterProvider>
    </React.StrictMode>
  );
}

I also placed it inside RouterProvider, but it didn't work.

I also tried passing it directly to the child's Path, but it didn't work.
How do I start from the middle?


Solution

  • For those of you who have the same problem, I will help you solve it.

    Please tell me if there are any mistakes.

    I would like to explain 'history.scroolRestration' first.

    In the first place, history.scrollRestoration is a property that controls the behavior of the browser to restore the scroll position.

    Specifying auto causes the browser to restore the scroll position. If you specify manual, you can control the scroll position restoration yourself instead of relying on the browser.

    In React, this 'history.scroolRestration' is usually set to auto.

    The problem for people looking at this question is that the scroll position is usually the same even if you go to another page, or the scroll position is from the beginning(I am the latter)

    If you run into this issue I recommend using enter link description here

    ”ScrollRestoration” can control scroll position Let me briefly explain how

    1. First, set history.scrollRestoration to manual to make the SPA, not the browser, responsible for restoring the scroll position.

    2. Monitor the pagehide event and record the scroll position at the screen transition timing to sessionStorage

    3. Get scroll position from sessionStorage and restore scroll position with window.scrollTo

    It is easy to use, just place near the Outlet. But I couldn't control the scroll position well

    I think there are two reasons. The first is that overflow: auto was placed on the child element rather than the parent element. The second is that it was asynchronously processed.

    window.scrollTo could no longer be used by placing overflow: auto on a child element

    I restored the scroll position before the asynchronous process finished loading and it ended up at the top.

    As a solution Use "ScrollRestoration" and set "history.scrollRestoration" to manual to save the scroll position of the child element and return the scroll position to the original position after the asynchronous process is completed.

    import { useBlocker } from 'react-router-dom';
    import React, { useState, useEffect,useRef } from 'react';
    
    export default function World() {
      const [worldData, setWorldData] = useState(null);
      const divRef = useRef(null);
    
      const blocker = (transition) => {
        if (divRef.current) {
          localStorage.setItem('scrollPosition', 
        divRef.current.scrollTop);
        }
        return false;  
      };
      useBlocker(blocker);
    
      useEffect(() => {
        const fetchData = async () => {
          setIsLoading(true);
          try {
            const data = await apiGetWorld(worldname);
            setWorldData(data);
            setIsLoading(false);
     
            if (divRef.current && savedScrollPosition) {
              divRef.current.scrollTop = 
            Number(savedScrollPosition);
            }
          } catch (error) {
            setError(error);
            setIsLoading(false);
          }
        };
      
        fetchData();
      }, [worldname]);
    
      const savedScrollPosition = 
      localStorage.getItem('scrollPosition');
    
      <div ref={divRef}>
    </div>
    }
    
    1. Use useRef to understand the scroll position of child elements
    2. Use useBlocker to save the scroll position in Localstorage before the page goes to another page
    3. When you return to the original page, get the scroll position from Localstorage and return the scroll position to the original position when the asynchronous process is finished.