Search code examples
reactjsreact-nativescrollview

how to scroll to a particular view inside react native scrollView hierarchy


as answered here in order to get Y offset of a View in ScrollView hierarchy, we need to use onLayout and keep on adding Y offset of each parent of View (whose offset is needed relative to ScrollView) until we reach ScrollView. This process is tedious in large component hierarchies. I am trying to write a wrapper around react native's ScrollView which will expose a method called scrollIntoView which takes reference of the View you need to scroll to and scrolls to that view.

For this I am finding some way to start from given View's reference and start traversing ancestors of given View and keep adding their Y offset value which is provided by onLayout callback. but i am not sure how to do this.

Any help will be appreciated.

Note: onLayout gives Y offset relative to parent component hence we need to sum for each ancestor of view in order to calculate Y offset of view relative to ScrollView


Solution

  • There is no elegant solution for it. An idea would be to get the position of View on page level then offset it with the first child in ScrollView. Reason for using the first child is because ScrollView can have an initial scroll value and we need to offset it properly.

    Assign ref to ScrollView and insert a dummy View as first child to measure the scroll position. Also assign a ref to the view you want to scroll to

    <ScrollView ref={scrollViewRef}>
      {/* collapsable={false} needed for measure to work properly on Android devices */}
      <View ref={topViewRef} collapsable={false} />
    
      {/* View you want to scroll to. 
          It could be inside a nested component */}
      <Text ref={labelRef} collapsable={false}>Scroll here</Text>
    
    </ScrollView>
    

    Then call this function and pass in the ScrollView Ref, Initial view Ref and the View you want to scroll to.

    export function scrollTo(scrollViewRef, topViewRef, viewToScrollToRef, offset = 0) {
        const scrollView = scrollViewRef.current;
        const topView = topViewRef.current;
        const viewToScrollTo = viewToScrollToRef.current;
        if (!scrollView || !topView || !viewToScrollTo) {
            //Throw an error or something
            return;
        }
    
        topView.measure((_ox, _oy, _width, _height, _px, topViewY) => {
            viewToScrollTo.measure((_ox2, _oy2, _width2, _height2, _px2, viewToScrollToY) => {
    
                scrollView.scrollTo({
                    x: 0,
                    y: viewToScrollToY - topViewY + offset,
                    animated: true,
                });
    
            });
        });
    }