Answer a question

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);

Answers

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!

Logo

React社区为您提供最前沿的新闻资讯和知识内容

更多推荐