【问题标题】:Maintaining state and scroll position in react在反应中保持状态和滚动位置
【发布时间】:2019-11-29 08:03:52
【问题描述】:

我正在尝试构建一个显示博客文章列表的组件。当用户单击帖子时,它会呈现显示帖子详细信息的组件。但是当用户在浏览器中点击后退按钮时,前一个带有帖子列表的组件会重新呈现,并且它会丢失之前的状态和滚动位置。有没有一种方法可以保存以前的状态和滚动位置,以便当用户点击后退按钮时它们处于相同位置并且帖子列表不会重新渲染并且也不会松开滚动位置?

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

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

【问题讨论】:

  • 您能否在 stackblitz 上创建包含所需信息的演示?所以我可以为您提供解决方案。
  • @ClueMediator 当然。我已经创建了一个包含两个组件的演示,这是链接。 stackblitz.com/edit/react-nhrwnn
  • 我已经为您更新了我的答案并创建了工作演示。请检查一下。

标签: javascript reactjs react-router


【解决方案1】:

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

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

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

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

默认可以设置scrollPosition的值为0

更新

这里我使用sessionStorage 来保持滚动位置以用于演示目的。你也可以使用 context API 或 redux store 来管理它。

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

希望对您有所帮助!

【讨论】:

    【解决方案2】:

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

    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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-07-27
          • 1970-01-01
          • 1970-01-01
          • 2011-03-22
          相关资源
          最近更新 更多