【问题标题】:React scroll to anchor when opening URL in browser在浏览器中打开 URL 时反应滚动到锚点
【发布时间】:2019-11-21 23:00:55
【问题描述】:

假设我有一个包含多个组件“评论”的组件“帖子”。当我输入这样的 URL 时,我想让该应用程序在带有该锚点的评论上向下滚动:

/post/:postId/#commentId

我已经在工作 postId route /post/:postId

我尝试使用 react-hash-link npm 包来实现它,但它没有按预期工作。

每条评论都有自己的 ID,设置在组件上,如下所示:

<div class="post">
   <div class="post-header">
      <div class="post-header-avatar">
        SOME TEXT
      </div>
      <div class="post-header-info">
        SOME TEXT
      </div>
   </div>
   <div class="post-content">
      <span>POST CONTENT</span>
   </div>
   <div class="post-likers-container">
      <div class="post-likers-header label">People who like this post</div>
      <div class="post-likers">
          SOME TEXT
      </div>
   </div>
   <div class="post-comments">
      <div class="comments ">
         <div class="comments-all label">Comments</div>
         <div class="comments">
            <div class="comment" id="5d27759edd51be1858f6b6f2">
               <div class="comment-content">
               COMMENT 1 TEXT
               </div>
            </div>
            <div class="comment" id="5d2775b2dd51be1858f6b720">
               <div class="comment-content">
               COMMENT 2 TEXT
               </div>
            </div>
            <div class="comment" id="5d2775ecdd51be1858f6b753">
               <div class="comment-content">
                COMMENT 3 TEXT
               </div>
            </div>
         </div>
      </div>
   </div>
</div>

例如,如果我打开这样的 URL:

/post/postId/#5d2775ecdd51be1858f6b753 

我想打开帖子页面,它会向下滚动到带有 # 锚点的评论。

有什么方法可以实现吗?

【问题讨论】:

  • 在您的实际代码中,您是如何生成 cmets 的?可能通过 .map(),或者这只是硬编码?
  • 我正在使用列表迭代生成 cmets 并使用 Comment 组件映射它们
  • @SaltyTeemooo 你对我下面的解决方案有任何疑问吗 :)

标签: javascript html reactjs


【解决方案1】:

我真的很喜欢你的解决方案@SaltyTeemooo。受此启发,我找到了一种更简单的方法,无需任何回调。

我的设置非常相似,所以假设我正在处理帖子和 cmets。

Post 我这样创建评论(简化)并传递anchorId:

<Comments anchorId={window.location.href.slice(window.location.href.indexOf("#") + 1)} props... />

Comments 中,我将锚 ID 传递到 Comment.js 中

<Comment anchorId={props.anchorId} props.../>

然后在Comment 中,我将当前元素滚动到视图中,如果它是链接的元素

import React, { useRef, useEffect } from 'react';

function Comment () {

    const comment = useRef(null); //to be able to access the current one

    useEffect(() => {
        if(props.anchorId === props.commentData.id)
        {
            comment.current.scrollIntoView({ behavior: "smooth" });
        }
    }, []) //same as ComponentDidMount

    
    return(
       <div id={props.commentData.id} ref={comment}> //here is where the ref gets set
           ...
       </div>
    )
}

【讨论】:

  • 这是我需要的!
【解决方案2】:

花了相当多的时间,但试试这个沙盒:https://codesandbox.io/s/scrollintoview-with-refs-and-redux-b881s

这将使您深入了解如何使用 URL 参数滚动到元素。

import React from "react";
import { connect } from "react-redux";
import { getPost } from "./postActions";

class Post extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      activeComment: null
    };

    this._nodes = new Map();
  }

  componentDidMount() {
    this.props.getPost(this.props.match.params.id);
    const path = window.location.href;
    const commentId = path.slice(path.indexOf("#") + 1);
    if (commentId) {
      this.setState({
        activeComment: commentId
      });
    }
  }

  componentDidUpdate(prevProps, prevState) {
    if (this.state.activeComment !== prevState.activeComment) {
      this.scrollToComment();
    }
  }

  scrollToComment = () => {
    const { activeComment } = this.state;
    const { comments } = this.props.posts.post;
    const nodes = [];
    //Array.from creates a new shallow-copy of an array from an array-like or iterable object
    Array.from(this._nodes.values()) //this._nodes.values() returns an iterable-object populated with the Map object values
      .filter(node => node != null)
      .forEach(node => {
        nodes.push(node);
      });

    const commentIndex = comments.findIndex(
      comment => comment.id == activeComment
    );

    if (nodes[commentIndex]) {
      window.scrollTo({
        behavior: "smooth",
        top: nodes[commentIndex].offsetTop
      });
    }
  };

  createCommentList = () => {
    const { post } = this.props.posts;
    const { activeComment } = this.state;

    if (post) {
      return post.comments.map((comment, index) => {
        return (
          <div
            key={comment.id}
            className={
              "comment " + (activeComment == comment.id ? "activeComment" : "")
            }
            ref={c => this._nodes.set(comment.id, c)}
          >
            {comment.text}
          </div>
        );
      });
    }
  };

  displayPost = () => {
    const { post } = this.props.posts;

    if (post) {
      return (
        <div className="post">
          <h4>{post.title}</h4>
          <p>{post.text}</p>
        </div>
      );
    }
  };

  render() {
    return (
      <div>
        <div>{this.displayPost()}</div>
        <div>{this.createCommentList()}</div>
      </div>
    );
  }
}

const mapStateToProps = state => {
  return {
    posts: state.posts
  };
};

const mapDispatchToProps = dispatch => {
  return {
    getPost: postId => {
      dispatch(getPost(postId));
    }
  };
};

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Post);

【讨论】:

    【解决方案3】:

    我设法为我的用例找到了简单的解决方案,而无需为 cmets 创建引用、传递它们等。因为我的组件层次结构是这样的:

    1. Post --> 渲染组件 Comments
    2. Comments --> 渲染 多个组件Comment 带有从Post 传递的道具数据

    Post 组件中我创建了函数:

    scrollToComment= () => {
        let currentLocation = window.location.href;
        const hasCommentAnchor = currentLocation.includes("/#");
        if (hasCommentAnchor) {
          const anchorCommentId = `${currentLocation.substring(currentLocation.indexOf("#") + 1)}`;
          const anchorComment = document.getElementById(anchorCommentId);
          if(anchorComment){
              anchorComment.scrollIntoView({ behavior: "smooth" });
          }
        }
      }
    

    然后我像这样渲染Comments

    <Comments limit={limit} post={post} scrollToComment={this.scrollToComment} />
    

    Comments 中,我在经过如下排序后生成 cmets:

    {sortedComments.map((comment, i) => <Comment key={i} {...comment} scrollToComment={this.props.scrollToComment}/> )}
    

    最后在Comment 组件中我在ComponentDidMount() 中执行scrollToComment

    if(this.props.scrollToComment)
        this.props.scrollToComment(this.props._id);
    

    之后,当我转到某个 URL 时,我可以流畅地滚动到 URL 的哈希部分中指定的评论。

    我尝试了@Christopher 解决方案,但它对我不起作用。

    【讨论】:

      【解决方案4】:

      测量...

      import React, { useEffect } from 'react';
      
      const MainApp = () => {
      
          const MyRef = React.createRef();
      
          useEffect(() => { // Same like ComponentDidMount().
              scrollTo();
          })
      
          const scrollTo = () => {
              window.scrollTo({
                  top:myRef.offsetTop, 
                  behavior: "smooth" // smooth scroll.
              });   
          }
      
              return (
                  <div ref={MyRef}>My DIV to scroll to.</div>
              )
      }
      

      【讨论】:

        猜你喜欢
        • 2010-12-26
        • 2017-07-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-07-13
        • 2013-02-05
        • 1970-01-01
        相关资源
        最近更新 更多