Search code examples
javascriptreactjsscrollref

Scroll boxes/bars linked by ref jumpy in React (especially mobile)


I have built a react component that maps a number of product cards in a horizontal row. When these cards are longer than the screen, overflow occurs and a scroll bar is visible at the bottom of the component allowing the user to scroll left and right along the cards. I also want a scroll bar to be visible above the cards, so I have added an additional div that takes its size from the product cards div (via ref) and presents an empty div with a scroll bar. I have then linked this divs scroll position with the products cards scroll position and vice versa to ensure both are the same. This works, but appears choppy/jumpy when scrolling especially on mobile.

I think maybe this is due to each onScroll event firing multiple times/contradicting each other, but cannot work out how to get this to work. Any help would be really appreciated!

The component that is rendered:

<div>
  {checkWidth() ? (
    <div
      className="ui link cards topscroll"
      ref={topScrollRef}
      onScroll={onScroll}
    >
      <div
        style={{
          minWidth: bottomScrollRef.current.scrollWidth,
          overflowX: "scroll",
        }}
      ></div>
    </div>
  ) : (
    ""
  )}
  <div
    ref={bottomScrollRef}
    onScroll={onScroll}
    className="ui link cards bottomscroll"
  >
    {renderList()}
  </div>
</div>

And the onScroll event handler:

const onScroll = (el) => {
  if (
    el.target.className === "ui link cards topscroll" &&
    bottomScrollRef.current.scrollLeft !== topScrollRef.current.scrollLeft
  ) {
    bottomScrollRef.current.scrollLeft = topScrollRef.current.scrollLeft;
  } else if (
    el.target.className === "ui link cards bottomscroll" &&
    topScrollRef.current.scrollLeft !== bottomScrollRef.current.scrollLeft
  ) {
    topScrollRef.current.scrollLeft = bottomScrollRef.current.scrollLeft;
  }
};

note: renderList() returns the products cards


Solution

  • The best way I found of doing this is using the npm package react-scroll-sync

    https://www.npmjs.com/package/react-scroll-sync

    This allows you to sync the two scrolls with minimal jumpyness (not perfect)