繁体   English   中英

在反应中维护 state 和滚动 position

[英]Maintaining state and scroll position in react

我正在尝试构建一个显示博客文章列表的组件。 当用户单击帖子时,它会呈现显示帖子详细信息的组件。 但是当用户在浏览器中点击后退按钮时,前一个带有帖子列表的组件会重新呈现,它会丢失前一个 state 并滚动 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?

这是我的博客列表组件代码:

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;

这是我的博客详细信息组件:

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

您必须使用window.pageYOffset在点击帖子时将滚动位置存储在state

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

一旦你点击后退按钮,你必须在componentDidMount的方法中设置窗口位置。

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

默认情况下,您可以将scrollPosition的值设置为0

更新

出于演示目的,我在这里使用sessionStorage来维护滚动位置。 您还可以使用上下文 API 或 redux 存储来管理它。

这是为您准备的工作演示。 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;

希望能帮到你!

这是使用钩子(功能组件)的答案

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

const Posts = () => {
  const [posts, setPosts] = 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');
      }
    }
  }, [posts]);

  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

只需检查上一个 URL 并调用 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;

以防万一,有人想对某些特定的 HTML 元素做同样的事情。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM