1

I'm trying to access this.refs so I can scroll to the top of a Flat List but it is undefined.

My render method is this:

render() {
    return (
      <View style={styles.container}>
        { this.state.loading &&
          <ActivityIndicator />
        }
        { !this.state.loading &&
          <FlatList
            ref='flatList'
            data={this.state.facebookPosts}
            onEndReachedThreshold={0}
            onEndReached={({ distanceFromEnd }) => {
              this._getFacebookPosts(this.state.nextPost);
            }}
            renderItem={({item}) => <FacebookPost
              date={item.created_time}
              message={item.message}
              image={item.attachments.data[0].media.image.src}
              url={item.link}
            />}
          />
      }
      </View>
    )
  }
}

As you can see, I have the ref set as 'flatList'.

I then have a tab bar that when you click on one of the tabs, it is meant to scroll to the top of the list. (I am trying just for not to console log out the this.refs but getting undefined)

static navigationOptions = {
    tabBarLabel: 'News',
    showIcon: true,
    tabBarIcon: ({ tintColor }) => (
      <Image
        source={require('../assets/images/newspaper-icon.png')}
        style={[styles.icon, {tintColor: tintColor}]}
      />
    ),
    tabBarOnPress: (scene, jumpToIndex) => {
      // this.refs.listRef.scrollToOffset({x: 0, y: 0, animated: true})
      console.log(this.refs);
      jumpToIndex(scene.index);
    }
  };

What do I need to do differently to get this.refs to not be undefined inside that onTabBarPress function?

Tenatious
  • 849
  • 4
  • 12
  • 32

3 Answers3

2

The problems in your code are

  1. ref takes a function not a string
  2. scrollToOffset does not use x, y as parameters. If you want to scroll to a certain index, use scrollToIndex instead.
  3. bind isLoading with refreshing is better than inject your FlatList after loading

The code below should work:

class MyListView extends React.Component {
  _listRef;
  ...

  render() {
    return (
      <View>
          <FlatList
            ref={ref => {this._listRef = ref; }}
            data={this.state.facebookPosts}
            renderItem={({item}) => <FacebookPost
              date={item.created_time}
              message={item.message}
              image={item.attachments.data[0].media.image.src}
              url={item.link}
            />}
            ...
            refreshing={this.state.loading}
          />
      }
      </View>
    )
  }

  scrollToIndex = (idx) => {
    this._listRef.scrollToIndex({ index: idx, animated: true });
  }
}
FisNaN
  • 2,517
  • 2
  • 24
  • 39
1

In my option, you only can access to refs that are in the same class. You do what you wanna do but using componentDidMount when it mounts you scroll the list

Stan SARR
  • 11
  • 2
  • The components don't get unmounted on tab change so I can't just fire something on componentDidMount every time. – Tenatious Sep 27 '17 at 15:32
  • @Tenatious are using a state manager like Redux or Mobx? You can check the navigation state if it's the tab that you want you scroll on `componentWillReceiveProps`. I'm not really sure but the `componentDidMount ` is fired everytime tab moves. – Stan SARR Sep 27 '17 at 15:45
0

It appears that you are trying to access a property on the class instance inside a static property which knows nothing about the instance. Static properties are tied to the class not the class instance and so their this keyword is not a reference to the class instance.

It looks like you are using react-navigation and so you could hack the instance (this) into your static navigationOptions property by doing this in componentDidMount.

componentDidMount() {
  this.props.navigation.setParams({ instance: this });
}

And then you can define your navigationOptions as a function like this to get access to your navigation params.

static navigationOptions = ({ navigation }) => ({
  tabBarLabel: 'News',
  showIcon: true,
  tabBarIcon: ({ tintColor }) => (
    <Image
      source={require('../assets/images/newspaper-icon.png')}
      style={[styles.icon, {tintColor: tintColor}]}
    />
  ),
  tabBarOnPress: (scene, jumpToIndex) => {
    navigation.state.params.instance.refs.listRef.scrollToOffset({x: 0, y: 0, animated: true})
    jumpToIndex(scene.index);
  }
});

This may not be the best way, but it is a way to accomplish it.

jasonmerino
  • 3,220
  • 1
  • 21
  • 38