Maintaining state and scroll position in react

I’m trying to build a component that shows a list of blog posts. When a user clicks on a post, it renders the component that shows the details of the post. But when the user hits the back button in the browser, the previous component with posts list re-renders and it looses the previous state and scroll position. Is there a way that I Can save the previous state and the scroll position so that when a user hits the back button they are at the same position and the post list is not re-rendered and doesn’t loose the scroll position too?

Here’s my blog list component code:

import axios from "axios";
import { Link } from "react-router-dom";

class About extends React.Component {
  state = { posts: [] };

  componentDidMount() {
    axios.get("https://jsonplaceholder.typicode.com/posts").then(res => {
      this.setState({ posts: res.data.slice(0, 10) });
    });
  }

  render() {
    const { posts } = this.state;

    const postsList = posts.length ? (
      posts.map(post => {
        return (
          <div className="card" key={post.id}>
            <div className="card-body">
              <Link to={"/view/" + post.id}>
                <h5 className="card-title">{post.title}</h5>
              </Link>
              <p className="card-text">{post.body}</p>
            </div>
          </div>
        );
      })
    ) : (
      <div className="text-danger text-center">No Posts yet...</div>
    );

    return <div>{postsList}</div>;
  }
}

export default About;

Here’s is my blog details component:

import React from "react";
import { withRouter } from "react-router-dom";
import axios from "axios";

class PostDetail extends React.Component {
  state = { post: null };

  componentDidMount() {
    let id = this.props.match.params.post_id;

    axios.get("https://jsonplaceholder.typicode.com/posts/" + id).then(res => {
      this.setState({ post: res.data });
    });
  }

  render() {
    const post = this.state.post ? (
      <div className="card border-primary">
        <div className="card-header">{this.state.post.title}</div>
        <div className="card-body text-primary">
          <p className="card-text">{this.state.post.body}</p>
        </div>
      </div>
    ) : (
      <div className="text-center text-danger">Loading Post...</div>
    );

    return <div>{post}</div>;
  }
}

export default withRouter(PostDetail);

Here is Solutions:

We have many solutions to this problem, But we recommend you to use the first solution because it is tested & true solution that will 100% work for you.

Solution 1

You have to store the scroll position in state on click of post with the use of window.pageYOffset

this.setState({
    scrollPosition: window.pageYOffset
});

And once you click on back button at that time you have to set the window position in the method of componentDidMount.

window.scrollTo(0, this.state.scrollPosition);

By default you can set the value of scrollPosition is 0.

Updated

Here I have used the sessionStorage to maintain the scroll position for demo purpose. You can also use the context API or redux store to manage it.

Here is the working demo for you.
https://stackblitz.com/edit/react-fystht

import React from "react";
import axios from "axios";
import { Link } from "react-router-dom";

class Posts extends React.Component {
  state = { posts: [] };

  componentDidMount() {
    axios.get("https://jsonplaceholder.typicode.com/posts").then(res => {
      this.setState({ posts: res.data.slice(0, 20) }, () => {
        this.handleScrollPosition();
      });
    });
  }

  // handle scroll position after content load
  handleScrollPosition = () => {
    const scrollPosition = sessionStorage.getItem("scrollPosition");
    if (scrollPosition) {
      window.scrollTo(0, parseInt(scrollPosition));
      sessionStorage.removeItem("scrollPosition");
    }
  };

  // store position in sessionStorage
  handleClick = e => {
    sessionStorage.setItem("scrollPosition", window.pageYOffset);
  };

  render() {
    const { posts } = this.state;

    const postsList = posts.length ? (
      posts.map(post => {
        return (
          <div className="card" key={post.id}>
            <div className="card-body">
              <Link to={"/view/" + post.id} onClick={this.handleClick}>
                <h5 className="card-title">{post.title}</h5>
              </Link>
              <p className="card-text">{post.body}</p>
            </div>
          </div>
        );
      })
    ) : (
      <div className="text-danger text-center">No Posts yet...</div>
    );

    return <div>{postsList}</div>;
  }
}

export default Posts;

Hope this will help you!

Solution 2

here is the answer using hooks(functional component)

import axios from 'axios';
import { Link } from 'react-router-dom';

const Posts = () => {
  const 

MySQL error – "You have an error in your SQL syntax"

The error message I got: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ”word’,’group’,’selfnote’) VALUES (‘item’,’a’,’note to self’)’ at line 1 The PHP code is: $toq=”INSERT INTO articles (‘word’,’group’,’selfnote’) VALUES (‘$ttle’,’$wrdr’,’$snote’)”; I was trying to find solutins, but […]

0 comments

ORDER BY datetime makes the query very slow

I am trying to pull data from multiple tables and when I user ORDER BY a datetime field it return the results after at least 10 seconds but if I do the same query without ORDER BY then it return the results for under 2 seconds. This is my current query SELECT ph.call_subject AS callSubject, […]

0 comments

Java newbie needs help in database connection

I’m new to Java and even newer to java database connections. I’ve managed to create a database connection and query a table when I put it in the Main class. Now that I’ve moved it into a new class called Connection I am getting errors: package lokate; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; import java.sql.ResultSet; […]

0 comments

How do I put backticks in a column name called key for hibernate

I have a MySQL DB table which I cannot modify but have to insert values into. It has a column called key which I need to insert into. Now, my .hbm.xml file has: <property name=”key” type=”string” column=”key” /> The insert query generated hence fails. On adding [key], the generated SQL now contained key but this […]

0 comments

Should we include sort column, primary key on composite index (MySQL)

Table (Simplified): +———————————————————————+ | id (Primary AI) | user_id | status | type | data | ip | +=====================================================================+ | 1 | 3 | 0 | abc | a-s-d | – | +———————————————————————+ | 2 | 1 | 0 | ooo | z-z-z | – | +———————————————————————+ | 3 | 3 | 0 | ooo […]

0 comments

Can I have a primary key without clustered index ? Also can I have multivalued clustered index?

Folks, I would like to understand the answer for the following questions: Can I have a primary key without clustered index ? ( I am aware that when we create primary key constraint on a column, it by default creates a clustered index. So in that case, how should I deactivate clustered index ?) Can […]

0 comments

MySQL UNION 2 queries containing ORDER BYs

I am trying to UNION two queries which both contain ORDER BY’s. As I have discovered, you can not order by queries that are part of a UNION. I just don’t know how else to do this query then. Let me explain what I’m trying to do. I am trying to select the 40 most […]

0 comments

Transient Failure handling in .net core 2.1 MVC for MySQL Database

services.AddDbContext<MyContext>(options => { options.UseSqlServer(mysqlConnection, sqlServerOptionsAction: sqlOptions => { sqlOptions.EnableRetryOnFailure( maxRetryCount: 10, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); }); }); I found this code snippet at: https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/implement-resilient-applications/implement-resilient-entity-framework-core-sql-connections My DB is MySQL 5.7 I changed the above code to : That means EnableRetryOnFailure is not available for MySQL DB. How do i set the retry, delay etc.. policies now? […]

0 comments

Can't create foreign key with ON DELETE SET DEFAULT

I can’t create foreign keys with ON DELETE SET DEFAULT, but if i use ON DELETE CASCADE then all works here is my sql CREATE TABLE person( customer_id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100) UNIQUE ); CREATE TABLE habits( customer_id INT AUTO_INCREMENT PRIMARY KEY, habit VARCHAR(100) UNIQUE ); INSERT INTO `test`.`habits` (`customer_id`, `habit`) VALUES (NULL, […]

0 comments

MySQL connection: globally or in object?

I have two PHP classes. One is for connecting to the database, building queries, executing them, and disconnecting from the database. The other class is for users: adding them, updating them, logging them in, etc. I’m debating whether I should connect to the database on the page globally and use that connection (passing the database […]

0 comments
= useState([]); const postsList = posts.length; useEffect(() => { axios.get('https://jsonplaceholder.typicode.com/posts').then((res) => { setPosts(res.data.slice(0, 20)); }); }, []); useEffect(() => { if (posts.length) { const scrollPosition = sessionStorage.getItem('scrollPosition'); if (scrollPosition) { window.scrollTo(0, parseInt(scrollPosition, 10)); sessionStorage.removeItem('scrollPosition'); } } },

MySQL error – "You have an error in your SQL syntax"

The error message I got: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ”word’,’group’,’selfnote’) VALUES (‘item’,’a’,’note to self’)’ at line 1 The PHP code is: $toq=”INSERT INTO articles (‘word’,’group’,’selfnote’) VALUES (‘$ttle’,’$wrdr’,’$snote’)”; I was trying to find solutins, but […]

0 comments

ORDER BY datetime makes the query very slow

I am trying to pull data from multiple tables and when I user ORDER BY a datetime field it return the results after at least 10 seconds but if I do the same query without ORDER BY then it return the results for under 2 seconds. This is my current query SELECT ph.call_subject AS callSubject, […]

0 comments

Java newbie needs help in database connection

I’m new to Java and even newer to java database connections. I’ve managed to create a database connection and query a table when I put it in the Main class. Now that I’ve moved it into a new class called Connection I am getting errors: package lokate; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; import java.sql.ResultSet; […]

0 comments

How do I put backticks in a column name called key for hibernate

I have a MySQL DB table which I cannot modify but have to insert values into. It has a column called key which I need to insert into. Now, my .hbm.xml file has: <property name=”key” type=”string” column=”key” /> The insert query generated hence fails. On adding [key], the generated SQL now contained key but this […]

0 comments

Should we include sort column, primary key on composite index (MySQL)

Table (Simplified): +———————————————————————+ | id (Primary AI) | user_id | status | type | data | ip | +=====================================================================+ | 1 | 3 | 0 | abc | a-s-d | – | +———————————————————————+ | 2 | 1 | 0 | ooo | z-z-z | – | +———————————————————————+ | 3 | 3 | 0 | ooo […]

0 comments

Can I have a primary key without clustered index ? Also can I have multivalued clustered index?

Folks, I would like to understand the answer for the following questions: Can I have a primary key without clustered index ? ( I am aware that when we create primary key constraint on a column, it by default creates a clustered index. So in that case, how should I deactivate clustered index ?) Can […]

0 comments

MySQL UNION 2 queries containing ORDER BYs

I am trying to UNION two queries which both contain ORDER BY’s. As I have discovered, you can not order by queries that are part of a UNION. I just don’t know how else to do this query then. Let me explain what I’m trying to do. I am trying to select the 40 most […]

0 comments

Transient Failure handling in .net core 2.1 MVC for MySQL Database

services.AddDbContext<MyContext>(options => { options.UseSqlServer(mysqlConnection, sqlServerOptionsAction: sqlOptions => { sqlOptions.EnableRetryOnFailure( maxRetryCount: 10, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); }); }); I found this code snippet at: https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/implement-resilient-applications/implement-resilient-entity-framework-core-sql-connections My DB is MySQL 5.7 I changed the above code to : That means EnableRetryOnFailure is not available for MySQL DB. How do i set the retry, delay etc.. policies now? […]

0 comments

Can't create foreign key with ON DELETE SET DEFAULT

I can’t create foreign keys with ON DELETE SET DEFAULT, but if i use ON DELETE CASCADE then all works here is my sql CREATE TABLE person( customer_id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100) UNIQUE ); CREATE TABLE habits( customer_id INT AUTO_INCREMENT PRIMARY KEY, habit VARCHAR(100) UNIQUE ); INSERT INTO `test`.`habits` (`customer_id`, `habit`) VALUES (NULL, […]

0 comments

MySQL connection: globally or in object?

I have two PHP classes. One is for connecting to the database, building queries, executing them, and disconnecting from the database. The other class is for users: adding them, updating them, logging them in, etc. I’m debating whether I should connect to the database on the page globally and use that connection (passing the database […]

0 comments
); return ( <> {postsList ? ( posts.map((post) => { return ( <div className="card" key={post.id}> <div className="card-body"> <Link to={'/view/' + post.id} onClick={() => sessionStorage.setItem('scrollPosition', window.pageYOffset) } > <h5 className="card-title">{post.title}</h5> </Link> <p className="card-text">{post.body}</p> </div> </div> ); }) ) : ( <div className="text-danger text-center">No Posts yet...</div> )} <div>{postsList}</div> </> ); }; export default Posts;

https://stackblitz.com/edit/react-cbl7in?file=Posts.js

Solution 3

Just Check Previous URL and call API:

import React, {Component} from 'react';
import axios from "axios";
import { Link } from "react-router-dom";

class About extends React.Component {

  constructor() {
    super();
    this.state = {
     posts: [],
    };
  }

componentDidMount() {
 var url = document.referrer
 if (url && !url.includes("/view/")) {
   axios.get("https://jsonplaceholder.typicode.com/posts").then(res => {
     this.setState({
       posts: res.data.slice(0, 10),
       isAboutPageOpen: true
     });
   });
 }
}

render() {
 const { posts } = this.state;

 const postsList = posts.length ? (
   posts.map(post => {
     return (
       <div className="card" key={post.id}>
         <div className="card-body">
           <Link to={"/view/" + post.id}>
             <h5 className="card-title">{post.title}</h5>
           </Link>
           <p className="card-text">{post.body}</p>
         </div>
       </div>
     );
    })
  ) : (
   <div className="text-danger text-center">No Posts yet...</div>
 );
 return <div>{postsList}</div>;
 }
}

export default About;

Solution 4

Just in case, someone want to do the same on some specific HTML elements.

const scrollYPosition = document.getElementsByClassName('grid-body')[0].scrollTop;
this.setState({
  toBeExpanded
},() => {
  document.getElementsByClassName('grid-body')[0].scrollTo(0, scrollYPosition);
});

Note: Use and implement solution 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply