question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

item.value.scrollToOffset not working for me

See original GitHub issue

ScrollOffset does not sync fo each route, below is my code

import React, {useState, useEffect, useRef} from 'react';
import {
  SafeAreaView,
  StyleSheet,
  View,
  Text,
  Dimensions,
  Animated,
  Image,
  StatusBar
} from 'react-native';
import {TabView, TabBar, SceneMap} from 'react-native-tab-view';

import Icon from 'react-native-vector-icons/FontAwesome';
import IconF from 'react-native-vector-icons/Feather';
import ProfileFlatList from '../components/ProfileFlatList.js';
import { Thumbnail, Button, Tab, Tabs, TabHeading } from 'native-base';


const DATA = [
    {image:require('../../assets/4.jpg'),isDraft:true,draftCount:'24',draftIcon:'inbox'},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/2.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/1.png'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/1.png'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/2.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false}
];

const DATA1 = [
    {image:require('../../assets/4.jpg'),isDraft:true,draftCount:'24',draftIcon:'inbox'},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/2.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/1.png'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/1.png'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/2.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false}
];
const TabBarHeight = 48;
const HeaderHeight = 390;
const tab1ItemSize = (Dimensions.get('window').width - 30) / 3;

class TabScene extends React.Component {
  render = () => {
    const windowHeight = Dimensions.get('window').height;
    const {
      numCols,
      data,
      renderItem,
      onGetRef,
      scrollY,
      onScrollEndDrag,
      onMomentumScrollEnd,
      onMomentumScrollBegin,
    } = this.props;
    return (
      <Animated.FlatList
        scrollToOverflowEnabled={true}
        numColumns={numCols}
        ref={onGetRef}
        scrollEventThrottle={16}
        onScroll={Animated.event(
          [{nativeEvent: {contentOffset: {y: scrollY}}}],
          {useNativeDriver: true},
        )}
        onMomentumScrollBegin={onMomentumScrollBegin}
        onScrollEndDrag={onScrollEndDrag}
        onMomentumScrollEnd={onMomentumScrollEnd}
        ItemSeparatorComponent={() => <View style={{height: 10}} />}
        ListHeaderComponent={() => <View style={{height: 10}} />}
        contentContainerStyle={{
          paddingTop: HeaderHeight + TabBarHeight,
          paddingHorizontal: 10,
          minHeight: windowHeight - TabBarHeight,
        }}
        showsHorizontalScrollIndicator={false}
        data={data}
        renderItem={renderItem}
        showsVerticalScrollIndicator={false}
        keyExtractor={(item, index) => index.toString()}
      />
    );
  };
}

const Profile = () => {
  const [tabIndex, setIndex] = useState(0);
  const [routes] = useState([
    {key: 'tab1', title: 'Tab1'},
    {key: 'tab2', title: 'Tab2'},
  ]);
  const scrollY = useRef(new Animated.Value(0)).current;
  let listRefArr = useRef([]);
  let listOffset = useRef({});
  let isListGliding = useRef(false);

  useEffect(() => {
    scrollY.addListener(({value}) => {
      const curRoute = routes[tabIndex].key;
      listOffset.current[curRoute] = value;
    });
    return () => {
      scrollY.removeAllListeners();
    };
  }, [routes, tabIndex]);

  const syncScrollOffset = () => {
    const curRouteKey = routes[tabIndex].key;
    listRefArr.current.forEach((item) => {
      // sync value except current route
      if (item.key !== curRouteKey) {
        // if header has not yet been collapsed, scroll to current offset
        if (scrollY._value < HeaderHeight && scrollY._value >= 0) {
          if (item.value) {
            item.value.scrollToOffset({
              offset: scrollY._value,
              animated: false,
            });
             // we should also update the offset here
            listOffset.current[item.key] = scrollY._value;
          }
       // if header has been collapsed, scroll to HeaderHeight
      } else if (scrollY._value >= HeaderHeight) {
          if (listOffset.current[item.key] < HeaderHeight ||
            listOffset.current[item.key] == null) {
            if (item.value) {
                item.value.scrollToOffset({
                  offset: HeaderHeight,
                  animated: false,
                });
                listOffset.current[item.key] = HeaderHeight;
              }
            }
        }
      }
    });
  };

  const onMomentumScrollBegin = () => {
    isListGliding.current = true;
  };

  const onMomentumScrollEnd = () => {
    isListGliding.current = false;
    syncScrollOffset();
  };

  const onScrollEndDrag = () => {
    syncScrollOffset();
  };

  const renderHeader = () => {
    const y = scrollY.interpolate({
      inputRange: [0, HeaderHeight],
      outputRange: [0, -HeaderHeight],
      extrapolateRight: 'clamp',
    });
    return (
      <Animated.View style={[styles.header, {transform: [{translateY: y}]}]}>
        <View style={{width: "100%",}}>
            <View style={{height:250,backgroundColor:'red'}}>
                <Image source={require('../../assets/2.jpg')} style={styles.topBG}></Image>
                <View style={styles.overlay} ></View>
            </View>
            <View style={{height:220, borderTopLeftRadius:30,borderTopRightRadius:30, marginTop:-40,backgroundColor:'#fff'}}>
                <View style={{flex:0.3,flexDirection:'row',}}>
                    <View style={{marginTop:-40,marginLeft:50,
                        marginBottom:15,
                        shadowColor: '#000',
                        borderRadius:600,
                        shadowOffset: {
                            width: 0,
                            height: 1,
                        },
                        shadowOpacity: 0.2,
                        shadowRadius: 1.50,
                        elevation:10}}>
                        <Thumbnail large source={require('../../assets/4.jpg')} style={{borderWidth:3,borderColor:'white',}}/>
                    </View>
                    <View style={{marginLeft:15, marginTop:10}}>
                        <Text>@itsandysworld</Text>
                        <Text style={{color:'#c6c6c6'}}>ID: MT1989861</Text>
                    </View>
                </View>
                <View style={{flex:1,justifyContent:'space-around'}}>
                    <View style={{flexDirection:'row',justifyContent:'space-evenly',paddingHorizontal:40,marginTop:20}}>
                        <View style={{marginHorizontal:15,alignItems:'center',}}>
                            <Text>504</Text>
                            <Text style={{color:'#898b90'}}>Following</Text>
                        </View>
                        <View style={{borderRightColor:'#f1f1f1',borderRightWidth:2,height:20,marginTop:10}}></View>
                        <View style={{marginHorizontal:15,alignItems:'center',}}>
                            <Text>210.8K</Text>
                            <Text style={{color:'#898b90'}}>Fans</Text>
                        </View>
                        <View style={{borderRightColor:'#f1f1f1',borderRightWidth:2,height:20,marginTop:10}}></View>
                        <View style={{marginHorizontal:15,alignItems:'center',}}>
                            <Text>2.8m</Text>
                            <Text style={{color:'#898b90'}}>Hearts</Text>
                        </View>
                    </View>
                    <View style={{flexDirection:'row',justifyContent:'space-evenly',marginTop:5,paddingHorizontal:40, marginTop:40}}>
                        <Button bordered style={{width:130},styles.outlineBtn}>
                            <Text style={styles.outlineText}>Message</Text>
                        </Button>
                        <Button bordered style={styles.outlineBtn}>
                            <IconF name="user-check" color="#000"></IconF>
                        </Button>
                        <Button bordered style={styles.outlineBtn}>
                            <IconF name="video" color="#000"></IconF>
                        </Button>
                    </View>
                    <View style={{padding:10,alignItems:'center', marginTop:30, marginBottom:5}}>
                        <Text style={{color:'#969498'}}>#AndyFam</Text>
                        <Text style={{color:'#969498'}}>What's up AndyFam <Icon name='heart' color='#969498'></Icon> Don't forget to</Text>
                        <Text style={{color:'#969498'}}>subscribe to my YouTube</Text>
                    </View>
                    <View style={{borderTopColor:'#efefef',borderTopWidth:1,width:'90%',marginTop:25, marginLeft:20,}}></View>
                </View>
            </View>
        </View>
      </Animated.View>
    );
  };

const rednerTab1Item = ({item, index}) => {
    return (
        <View style={{ flex: 1,
            marginLeft: index % 3 === 0 ? 0 : 10,
            width: tab1ItemSize,
            height: tab1ItemSize,
        }}>
            <Image style={{justifyContent: 'center',
                    alignItems: 'center',
                    width: '100%',
                    height: '100%',
                    borderRadius:8,
                    resizeMode: 'cover',}}
                source={item.image } />
            {item.isDraft &&
                <View style={{
                    ...StyleSheet.absoluteFillObject,
                    backgroundColor: 'rgba(0,0,0,0.6)',
                    borderRadius:8,
                    justifyContent: 'center',
                    alignItems: 'center',
                }} >
                    <IconF name={item.draftIcon} color='#fff' size={20} style={{marginTop:35}}></IconF>
                    <Text style={{color:'#fff',marginTop:5,fontSize:12}}>Draft</Text>
                    <Text style={{color:'#fff',fontSize:12}}>{item.draftCount}</Text>
                </View>
            }
            <View style={styles.bottomIcon}>
                <View style={styles.bottomIconRow} >
                    <Text style={{color:item.textColor,fontSize:12}}><Icon name={item.viewIcon} color={item.iconColor} size={8}></Icon> {item.view}</Text>
                    <Text style={{color:item.textColor,fontSize:12}}><Icon name={item.heartIcon} color={item.iconColor} size={8}></Icon> {item.heart}</Text>
                </View>
            </View>
        </View>
    );
  };

  const renderLabel = ({route, focused}) => {
    return (
        <>
            {route.key == 'tab1' &&
                <Image height={20} width={20} style={{tintColor:focused ?"#fd0d3a" :"#c3c3c3"}} source={require('../../assets/profile_list_icon.png')}/>
            }
            {route.key == 'tab2' &&
                <IconF size={20} name="heart" color={focused ?"#fd0d3a" :"#c3c3c3"} />
            }
        </>
    );
  };

  const renderScene = ({route}) => {
    const focused = route.key === routes[tabIndex].key;
    let numCols;
    let data;
    let renderItem;
    switch (route.key) {
      case 'tab1':
        numCols = 3;
        data = DATA;
        renderItem = rednerTab1Item;
        break;
      case 'tab2':
        numCols = 3;
        data = DATA1;
        renderItem = rednerTab1Item;
        break;
      default:
        return null;
    }
    return (
      <TabScene
        numCols={numCols}
        data={data}
        renderItem={renderItem}
        scrollY={scrollY}
        onMomentumScrollBegin={onMomentumScrollBegin}
        onScrollEndDrag={onScrollEndDrag}
        onMomentumScrollEnd={onMomentumScrollEnd}
        onGetRef={(ref) => {
          if (ref) {
            const found = listRefArr.current.find((e) => e.key === route.key);
            if (!found) {
              listRefArr.current.push({
                key: route.key,
                value: ref,
              });
            }
          }
        }}
      />
    );
  };

  const renderTabBar = (props) => {
    const y = scrollY.interpolate({
      inputRange: [0, HeaderHeight],
      outputRange: [HeaderHeight, 0],
      extrapolateRight: 'clamp',
    });
    return (
      <Animated.View
        style={{
          top: 0,
          zIndex: 1,
          position: 'absolute',
          transform: [{translateY: y}],
          width: '100%',
        }}>
        <TabBar
          {...props}
          onTabPress={({route, preventDefault}) => {
            if (isListGliding.current) {
              preventDefault();
            }
          }}
          style={styles.tab}
          renderLabel={renderLabel}
          indicatorStyle={styles.indicator}
        />
      </Animated.View>
    );
  };

  const renderTabView = () => {
    return (
      <TabView
        onIndexChange={(index) => setIndex(index)}
        navigationState={{index: tabIndex, routes}}
        renderScene={renderScene}
        renderTabBar={renderTabBar}
        initialLayout={{
          height: 0,
          width: Dimensions.get('window').width,
        }}
      />
    );
  };

  return (
    <>
    <StatusBar
        backgroundColor = "#fff"
        barStyle = "light-content"
    />
    <SafeAreaView style={{flex: 1, backgroundColor:'#fff'}}>
      <View style={{flex: 1}}>
        {renderTabView()}
        {renderHeader()}
      </View>
    </SafeAreaView>
    </>
  );
};

const styles = StyleSheet.create({
    header: {
        top: 0,
        height: 300,
        width: '100%',
        backgroundColor: '#40C4FF',
        alignItems: 'center',
        justifyContent: 'center',
        position: 'absolute',
    },
    tab: {elevation: 0, shadowOpacity: 0, backgroundColor: '#fff'},
    indicator: {backgroundColor: '#fd0d3a'},
    container: {
        flex: 1,
        flexDirection: "column",
        backgroundColor:'#fff'
    },
    row:{
        flexDirection:"column",
        margin:30,
    },
    overlay: {
        ...StyleSheet.absoluteFillObject,
        backgroundColor: 'rgba(0,0,0,0.4)',
    },
    topBG:{
        // flex: 1,
        width:'100%',
        resizeMode: 'cover',
    },
    outlineBtn:{
        borderColor:'#c3c3c3',
        paddingHorizontal:15,
        borderRadius: 3,
        alignItems:'center',
        height:45,
        // width:130
    },
    outlineText:{
        color:'#000',
        fontSize: 15,
        textAlign: 'center',
        padding:10,
        alignSelf:'center',
        backgroundColor: 'transparent'
    },
    tabBarUnderlineStyle:{
        backgroundColor:'#fff',
        height:2.5,
    },
    imageThumbnail: {
        justifyContent: 'center',
        alignItems: 'center',
        height: 100,
    },
    bottomIconRow:{
        flex:1,
        flexDirection:'row',
        justifyContent: 'space-evenly',
        paddingHorizontal:0,
    },
    bottomIcon:{
        //backgroundColor:'green',
        position:'absolute',
        bottom:0,
        width:'100%',
        marginBottom:8
    }
});

export default Profile;

Issue Analytics

  • State:open
  • Created 3 years ago
  • Comments:18 (7 by maintainers)

github_iconTop GitHub Comments

1reaction
doanbhcommented, Sep 25, 2020

I have found a solution. Because ScrollView has no scrollToOffset function but only scrollTo function, it is necessary to check the current component type run time if it is ScrollView.

0reactions
JungHsuancommented, Jul 10, 2021

Hi @JungHsuan

i am using react-native-keyboard-aware-scroll-view and converted into const AnimatedKeyboardAwareScrollView = Animated.createAnimatedComponent( KeyboardAwareScrollView, ); for the tab animation and when i use as suggested above code snippet i got attached errror. Screenshot 2021-06-12 at 1 38 21 PM

Same error occuer when i use item.value.scrollToOffse.

Hi @aroravinay4 Could you call scrollToOffset by getting the ref of AnimatedKeyboardAwareScrollView?

const scrollViewRef = useRef(null);
<AnimatedKeyboardAwareScrollView 
   ref={ref => scrollViewRef.current =  ref } 
/>

and try: scrollViewRef.current.scrollToOffset()

scrollToOffset is a props of VirtualizedList Maybe you should try scrollTo for react-native-keyboard-aware-scroll-view

Read more comments on GitHub >

github_iconTop Results From Across the Web

In react-native scrollToOffset in flat list is not working properly?
In flatlist scroll to offset is not working properly when the no of items are less than 3. If the no of items...
Read more >
scrollToOffset not working ! : r/reactnative - Reddit
I have a flatList with a ref,when i call scrollToOffSet it doesnt work properly,it moves to the first item of the flatList ,what...
Read more >
react native flatlist infinite loop - You.com | The AI Search ...
The FlatList (paging enabled, horizontal) has 3 to 7 items in the data array ... The problem is, I would like to update...
Read more >
FlatList scrollToOffset bug - Expo Snack
FlatList scrollToOffset bug. Shows the broken behaviour of scrollToOffset function, which ignores the y value and always scrolls to top of the FlatList....
Read more >
Writing custom strategy for Angular CDK virtual scroll
I'm a devoted Angular developer and a musician. I use one thing to help me not to starve while doing another and I...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found