Home > Software engineering >  How to prevent react from auto scrolling the page on re-render?
How to prevent react from auto scrolling the page on re-render?

Time:08-28

I faced with a strange annoying issue with React - React automatically scrolls the page on re-render.

Demo app on CodeSandbox - https://codesandbox.io/s/react-scroll-bug-demo-y7u50j?file=/src/App.js

Steps to reproduce:

  • Scroll the page to the middle of list, so its first element is not visible anymore.
  • Wait for re-render to happen.
  • See your page auto-scrolled so the first element is visible again.

Issue - React automatically scrolls the page when these conditions are met:

  1. I have a vertical list of items.
  2. List re-rendered (items change their order).
  3. List is visible on the screen but the first element is not visible (it is above the screen).

So, when the list of items is not visible on screen - no auto scrolling issues. Also, when top of the list is below the top of my screen - no auto scrolling happen as well.

I have created a Vanilla JavaScript app to test if it is Chrome-specific behaviour - no bug, all good - with Vanilla JS there is no scrolling happen when the list of items re-rendered on the screen. It happens only with React app and is very annoying.

As you can see from my CodeSandbox demo:

  • there is no scroll call anywhere but it still happens.
  • I don't click anything - just scroll the page to the middle of list and see your page automatically scrolled to the top.
  • Height of the list is unchanged. No layout jump should happen here.

Expected behaviour - no scrolling happen when the list is re-rendered.

Any idea how to prevent such auto scrolling?

CodePudding user response:

Looks like react is getting confused with keys. If using random values, everything works as expected:

<div key={Math.random()} className="item">

Update

This behavior is called scroll anchoring. You can disable it like this:

.container {
  ...
  overflow-anchor: none;
}

CodePudding user response:

Hello you can create a component i tried at your codeSandbox and its working

import React, { useEffect, useRef, useState } from "react";
import "./styles.css";

const genList = (size = 10) => {
  return Array(size)
    .fill(1)
    .map((_, i) => i)
    .sort(() => (Math.random() > 0.5 ? 1 : -1));
};

/**
 * Steps to reproduce the issue:
 * 1. Scroll page.
 * 2. Wait for re-render of the list.
 * 3. See window scrolled to top of list when list re-rendered.
 */

export default function App() {
  const ref = useRef();
  const [, setRenderTrigger] = useState(0);
  useEffect(() => {
    const timer = setInterval(() => {
      setRenderTrigger((i) => i   1);
    }, 1e3);
    return () => {
      clearInterval(timer);
    };
  }, []);

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <div className="container">
        {/* Adjust list length so it long enough for scroll */}

        {genList(10).map((e) => {
          return <List> {e}</List>;
        })}
      </div>
    </div>
  );
}

const List = ({ children }) => {
  return (
    <div key={children} className="item">
      {children}
    </div>
  );
};
  • Related