react-native: RefreshControl does not work correctly on state change

React Native version: 0.59.8

Steps To Reproduce

  1. Pull down on list, refresh control appears (as it should)
  2. Press button on top of list to load data, refresh control does not appear even though refreshing is true. You can also this is true by pressing the button first, the pulling down, then finally pressing the button again.

Describe what you expected to happen: The refresh control should appear whenever refreshing is true

Snack, code example, or link to a repository: https://snack.expo.io/ByZXxOuhV

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 20
  • Comments: 26

Commits related to this issue

Most upvoted comments

still an issue on v0.62.2 when <RefreshControl /> is enabled (with local state) from a useEffect function.

If anyone is still having problems with this:

I found a workaround that is just enabling the bounce.

I had bounces={false} and after removing it, it’s working on iOS now(Android was already working)

But in my case, the loading didn’t appear not even once.

Opened Pr ^

same issue ,why so much bugs in basic componentπŸ™ƒπŸ™ƒπŸ™ƒπŸ™ƒπŸ™ƒπŸ™ƒπŸ™ƒπŸ™ƒπŸ™ƒπŸ™ƒπŸ™ƒπŸ™ƒπŸ™ƒπŸ™ƒπŸ™ƒπŸ™ƒπŸ™ƒπŸ™ƒπŸ™ƒπŸ™ƒπŸ™ƒπŸ™ƒπŸ™ƒπŸ™ƒ

Can confirm, it is really annoying because there are no actual workarounds if you need to:

  • Keep pull to refresh functionality
  • Show the loading indicator when the refresh is triggered programmatically (e.g. the user searched something)

I noticed it only happens on iOS (I tested on Android 9.0.0 and it works properly)

This is still an issue

The issue is still there in iOS. RN ver: 0.71.8

same problem here, refreshControl does not show first time in useEffect even i set showState to true

refreshControl={
          <RefreshControl
            tintColor={theme.mainOrangeColor}
            refreshing={isRefreshing}
            onRefresh={() => {
              console.log('onrefresh')
              skip = 0
              _getRoomList()
            }}
          /> 
        }

I had to use a very hacky and very wrong workaround to make it work.

Basically, I noticed that it worked the first time (usually while loading the initial data at screen startup) but stopped working after that. So, I force the component (a FlatList in my case) to re-render completely after each successful refreshcontrol appearance, by changing its key attribute.

Honestly, it sucks, and it could not be feasible on lists with a lot of components to re render, but in my case it works well enough.

export default class extends React.Component {
  flastListKey = 0

  constructor(props) {
    super(props)
    this.state = {
      loading: false
    }
  }

  render () {
    <FlatList
      key={this.flatListKey}
      refreshing={this.state.loading}
      onRefresh={() => this.refreshData()}
      {... other props}
      />
  }

  refreshData = () => {
    this.setLoading(true)

    // DO stuff

    setTimeout(() => {
      this.setLoading(false)
    }, 2000)
  }

  setLoading = (loading: boolean) => {
    let shouldResetFlatlist = this.state.loading && !loading

    this.setState(
      {
        loading: loading,
      },
      () => {
        if (shouldResetFlatlist) {
          this.resetFlatlist()
        }
      },
    )
  }

  resetFlatlist = () => {
    if (Platform.OS === 'ios') {
      this.flatListKey = Math.random() * 1000
      setTimeout(() => {
        this.forceUpdate()
      }, 250)
    }
  }
}

The timeout is necessary to wait for the RefreshControl animation to finish