简体   繁体   中英

Using a ref to find and scroll to the top position of child element in React

I hope my approach to this is correct but I am trying to build a component in React where a bunch of posts are rendered in a column view stacked on top of one another, each individual post is set to 100vh.

Psuedo Code

<container ref={postRef}>
   <post 1/>
   <post 2/>
   <post 3/>
</container>

I have a ref on the <container/> and from another component's onClick function I get the current index of the <post/> I clicked on, and then find that element in the dom.

  useEffect(() => {
    if (postRef.current && indexOfClickedPost) {
      const refBounds = postRef.current.children[indexOfClickedPost].getBoundingClientRect()

      window.scrollTo({
        top: // which unit goes here?
      });
    }
  })

my end goal is to scroll my <container/> component to the very top of the post relative to the one that was just clicked.

Ex. if the user clicks a post with an index of 2, the page will scroll to where the post 2 div begins

I am not sure which unit I am to put in my window.scrollTo to get that effect. Putting refBounds.top does not yield the results I want, it seems to be doing nothing at all from what I can visibly see. Any advice would be great. Thanks!

I propose two different approaches, the first one follows your trial, and it makes use of getBoundingClientRect() to calculate the right distance from top and scrollTo to scroll to that element:

function App() {
  const [currPostIdx, setCurrPostIdx] = useState(0);
  const containerRef = useRef();

  const selectPost = (idx) => {
    setCurrPostIdx(idx);
  };

  useEffect(() => {
    const el = containerRef.current.children[currPostIdx];
    const top = window.pageYOffset + el.getBoundingClientRect().top;
    console.log(currPostIdx, top);
    window.scrollTo(0, top);
  }, [currPostIdx]);

  return (
    <>
      <ul className="navbar">
        {posts.map((p, i) => (
          <li key={p.title} onClick={() => selectPost(i)}>
            {p.title}
          </li>
        ))}
      </ul>
      <div ref={containerRef}>
        {posts.map((p, idx) => (
          <Post key={p.title} post={p} idx={idx} />
        ))}
      </div>
    </>
  );
}

const Post = ({ idx, post }) => (
  <div
    id={post.title}
    className="post"
    style={{ backgroundColor: `#${idx + 5}${idx * 3}${idx * 4}` }}
  >
    <h4>{post.title}</h4>
  </div>
);

Demo HERE

The seond approach makes use of hash navigation and hence it has not to calculate the position of the element manually:

function App() {
  return (
    <div>
      <div className="navbar">
        {posts.map((p) => (
          <a href={`#${p.title}`}>{p.title}</a>
        ))}
      </div>
      {posts.map((p, idx) => (
        <Post key={p.title} post={p} idx={idx} />
      ))}
    </div>
  );
}

const Post = ({ idx, post }) => (
  <div
    id={post.title}
    className="post"
    style={{ backgroundColor: `#${idx + 5}${idx * 3}${idx * 4}` }}
  >
    <h4>{post.title}</h4>
  </div>
);

Demo HERE

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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