Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React scroll position jumps on state update

Trying to implement a simple show more button with react using useState hook. Everything is working fine except that when the content is toggled on the vertical scroll position jumps down the size of the div that was shown. In other words, the content that is shown is positioned above the "Show More" button instead of extending the text and keeping the scroll position at the beginning of the text. I have searched everything and everywhere but was unable to find an elegant solution without remembering and restoring scroll positions. Here is a sample code that shows my problem.

import {useState} from "react"

export default function Test() {
    let text = (
        <p>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec aliquet porttitor vulputate. Morbi pellentesque blandit est vel
            dictum. Fusce mattis dolor in diam tempor commodo. Integer eu consequat magna. Cras placerat magna vitae nunc auctor, quis
            sodales ex hendrerit. Interdum et malesuada fames ac ante ipsum primis in faucibus. Duis dapibus condimentum elementum.
            Phasellus ultrices quis quam vel dignissim. Suspendisse ac sapien iaculis, suscipit nunc vel, commodo lorem. Vestibulum a arcu
            ut mi commodo ullamcorper. Vestibulum massa nunc, sodales a diam et, condimentum finibus quam. Nulla aliquet lacus id rhoncus
            maximus. Phasellus sed vehicula erat, eget suscipit ipsum. Donec nulla arcu, luctus nec mattis eu, cursus sit amet libero.
            Nullam at egestas orci. Sed tempus convallis enim vitae rutrum. Sed mattis vel dolor eu laoreet. Nam a aliquet massa, eu
            vulputate lacus. Fusce condimentum, eros in scelerisque dictum, turpis nisl sagittis diam, auctor blandit massa nunc eget purus.
            Praesent vitae ultrices risus. Vivamus sed leo ipsum. Maecenas rhoncus, urna sit amet interdum maximus, risus odio ullamcorper
            aug
        </p>
    )

    const [show, setShow] = useState(false)

    return (
        <>
            <div style={{height: 1000, backgroundColor: "red"}}></div>
            <button onClick={() => setShow(!show)}>THIS IS HOW I WANT IT TO WORK</button>
            <div style={{width: "20%"}}>{show ? text : "Not showing anything"}</div>
            <button onClick={() => setShow(!show)}>BUT ON THIS BUTTON THAT IS BELOW TEXT</button>
            <div style={{height: 1000, backgroundColor: "blue"}}></div>
        </>
    )
}

Runnable code snippet:

function Test() {
    let text = (
        <p>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec aliquet porttitor vulputate. Morbi pellentesque blandit est vel
            dictum. Fusce mattis dolor in diam tempor commodo. Integer eu consequat magna. Cras placerat magna vitae nunc auctor, quis
            sodales ex hendrerit. Interdum et malesuada fames ac ante ipsum primis in faucibus. Duis dapibus condimentum elementum.
            Phasellus ultrices quis quam vel dignissim. Suspendisse ac sapien iaculis, suscipit nunc vel, commodo lorem. Vestibulum a arcu
            ut mi commodo ullamcorper. Vestibulum massa nunc, sodales a diam et, condimentum finibus quam. Nulla aliquet lacus id rhoncus
            maximus. Phasellus sed vehicula erat, eget suscipit ipsum. Donec nulla arcu, luctus nec mattis eu, cursus sit amet libero.
            Nullam at egestas orci. Sed tempus convallis enim vitae rutrum. Sed mattis vel dolor eu laoreet. Nam a aliquet massa, eu
            vulputate lacus. Fusce condimentum, eros in scelerisque dictum, turpis nisl sagittis diam, auctor blandit massa nunc eget purus.
            Praesent vitae ultrices risus. Vivamus sed leo ipsum. Maecenas rhoncus, urna sit amet interdum maximus, risus odio ullamcorper
            aug
        </p>
    )

    const [show, setShow] = React.useState(false)

    return (
        <div>
            <div style={{height: 1000, backgroundColor: "red"}}></div>
            <button onClick={() => setShow(!show)}>THIS IS HOW I WANT IT TO WORK</button>
            <div style={{width: "20%"}}>{show ? text : "Not showing anything"}</div>
            <button onClick={() => setShow(!show)}>BUT ON THIS BUTTON THAT IS BELOW TEXT</button>
            <div style={{height: 1000, backgroundColor: "blue"}}></div>
        </div>
    )
}

ReactDOM.render(<Test/>, document.getElementById("test"));
<body>
  <div id="test"></div>

  <!-- React.js CDN links -->
  <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

  <!-- Babel (essential for JSX syntax - https://babeljs.io/docs/en/#jsx-and-react) -->
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

</body>
like image 853
PS1TD Avatar asked Aug 23 '26 11:08

PS1TD


1 Answers

I have solved it! In Chrome CSS property overflow-anchor is auto by default which in turn makes the clicked button a scroll anchor. So when the new content is loaded the window scrolls with the anchor. In order to fix this behavior you can set overflow-anchor: none; globally or to the button itself.

function Test() {
    let text = (
        <p>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec aliquet porttitor vulputate. Morbi pellentesque blandit est vel
            dictum. Fusce mattis dolor in diam tempor commodo. Integer eu consequat magna. Cras placerat magna vitae nunc auctor, quis
            sodales ex hendrerit. Interdum et malesuada fames ac ante ipsum primis in faucibus. Duis dapibus condimentum elementum.
            Phasellus ultrices quis quam vel dignissim. Suspendisse ac sapien iaculis, suscipit nunc vel, commodo lorem. Vestibulum a arcu
            ut mi commodo ullamcorper. Vestibulum massa nunc, sodales a diam et, condimentum finibus quam. Nulla aliquet lacus id rhoncus
            maximus. Phasellus sed vehicula erat, eget suscipit ipsum. Donec nulla arcu, luctus nec mattis eu, cursus sit amet libero.
            Nullam at egestas orci. Sed tempus convallis enim vitae rutrum. Sed mattis vel dolor eu laoreet. Nam a aliquet massa, eu
            vulputate lacus. Fusce condimentum, eros in scelerisque dictum, turpis nisl sagittis diam, auctor blandit massa nunc eget purus.
            Praesent vitae ultrices risus. Vivamus sed leo ipsum. Maecenas rhoncus, urna sit amet interdum maximus, risus odio ullamcorper
            aug
        </p>
    )

    const [show, setShow] = React.useState(false)

    return (
        <div>
            <div style={{height: 1000, backgroundColor: "red"}}></div>
            <button onClick={() => setShow(!show)}>THIS IS HOW I WANT IT TO WORK</button>
            <div style={{width: "20%"}}>{show ? text : "Not showing anything"}</div>
            <button onClick={() => setShow(!show)}>BUT ON THIS BUTTON THAT IS BELOW TEXT</button>
            <div style={{height: 1000, backgroundColor: "blue"}}></div>
        </div>
    )
}

ReactDOM.render(<Test/>, document.getElementById("test"));
body {
    overflow-anchor: none;
}
<body>
  <div id="test"></div>

  <!-- React.js CDN links -->
  <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

  <!-- Babel (essential for JSX syntax - https://babeljs.io/docs/en/#jsx-and-react) -->
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

</body>
like image 197
PS1TD Avatar answered Aug 26 '26 02:08

PS1TD



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!