Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: issue #7001 #7002

Closed
wants to merge 2 commits into from
Closed

Conversation

godness84
Copy link

It fixes the issue #7001

@facebook-github-bot
Copy link
Contributor

By analyzing the blame information on this pull request, we identified @kmagiera, @andreicoman11 and @dmmiller to be potential reviewers.

@facebook-github-bot facebook-github-bot added GH Review: review-needed CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. labels Apr 15, 2016
@ghost
Copy link

ghost commented May 9, 2016

@godness84 updated the pull request.

@ghost
Copy link

ghost commented May 12, 2016

@kmagiera would you mind taking a look at this pull request? It's been a while since the last commit was reviewed.

@ghost ghost added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jul 12, 2016
@mkonicek
Copy link
Contributor

mkonicek commented Sep 9, 2016

@godness84 I'm not sure I understand the issue well - could you show a GIF of how it's broken?

Sorry for the long delay!

@mkonicek
Copy link
Contributor

mkonicek commented Sep 9, 2016

If you use a normal <ScrollView> instead does it work?

@ghost ghost added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Sep 9, 2016
@godness84
Copy link
Author

Yes, definitely. Using a normal <ScrollView> it works.
Try this piece of code:

import React, { Component } from 'react';
import {
  AppRegistry,  
  StyleSheet,
  Text,
  View,
  ListView,
  ScrollView,
  RecyclerViewBackedScrollView,
  ViewPagerAndroid,
} from 'react-native';

class Issue7001 extends Component {
  constructor(props){
    super(props);

    var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
    var rows = [];
    for (var i=0; i<100; i++){
      rows.push(i);
    }

    this.state = {
      dataSource: ds.cloneWithRows(rows),
    };
  }

  renderItem(item){
      return <Text style={{color: '#000000', fontSize: 20, flex: 1, textAlign: 'center'}}>{item}</Text>;
  }

  render() {
    return (
      <View style={styles.container}>
        <ViewPagerAndroid
          initialPage={0}
          style={{flex: 1}}
          >
          <View>
            <ListView
              dataSource={this.state.dataSource}
              renderRow={this.renderItem.bind(this)}
              style={styles.listView}
              pageSize={1}
              initialListSize={10}
              renderScrollComponent={props => {
                //return <ScrollView {...props} />; uncommenting this line it will work as expected
                return <RecyclerViewBackedScrollView {...props} />;
              }}
            />
          </View>
        </ViewPagerAndroid>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
  },
  listView: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
  },
});

AppRegistry.registerComponent('Issue7001', () => Issue7001);

@andreicoman11
Copy link
Contributor

Neither the ScrollView, nor the HorizontalScrollView have this issue, but neither of them override onAttachedToWindow in the way it's done here. That makes me think that the problem is coming from somewhere else and this is not the correct fix. Do you mind explaining how you got to this fix?

@godness84
Copy link
Author

In ReactNative the items are added to the scrollview while scrolling, Just few initial items are added to the scrollview at startup (it depends on the pagesize). When the RecycleView is scrolled, new items are added. These items are added to the adapter and the RecycleView is informed that it should refresh its content. The RecycleView enqueues these updates and performs them if the layout pass has been happened ('mFirstLayoutComplete' stores this information) otherwise it will wait for the layout pass to happen.

ReactNative uses its own layout system and bypass the normal one, it layouts the views before adding them to the view hierarchy. The RecycleView is expecting to be added to the hierarchy and that a layout pass will occur. RecycleView overrides the 'onAttachedToWindow' method and set the value of mFirstLayoutComplete to false: it's expecting that the layout pass will happen soon or later, but it won't, because ReactNative uses its own system.

Indeed, if you rotate the device a new layout pass is triggered, and the RecycleView will show the new items added by React, and it will always show new items added during scrolling, because mFirstLayoutComplete will remain true.

My fix is simulating a new layout pass calling the onLayout method, preserving the values of the layout already calculated by React.

@godness84
Copy link
Author

any news?

@lacker
Copy link
Contributor

lacker commented Oct 29, 2016

I feel like you have explained why adding an extra onLayout call fixes this particular issue, but this does not explain why it is correct to call onLayout here in general. onLayout is supposed to be invoked on mount and layout changes. Either onAttachedToWindow is considered a mount and layout change, or it is not. So it does not seem like the behavior of ScrollView and RecyclerViewBackedScrollView, in terms of when they fire onLayout, should be different here.

@hramos
Copy link
Contributor

hramos commented Nov 7, 2016

@godness84 still planning to ship this?

@godness84
Copy link
Author

@hramos if the other option is to leave the issue unfixed, yes, I would like to see it shipped.

When I wrote this fix, I already knew that it would have seemed a hack. The difficulties to make it shipped mean that the code review of ReactNative is quite hard, and I appreciate it.

These are my thoughts about this fix:

  1. ScrollView and RecyclerView add views in a very different way, the latter is using an Adapter. so it's acceptable that internally they behave differently.
  2. ReactScrollView(that extends ScrollView) is also using some fixes for the layout (eg. 'onLayout' method at lines 125-128)
  3. RecyclerView overrides onAttachedToWindow method and it flags itself as the layout pass has not happened still, because normally the layout pass happens after onAttachedToWindow is called - but not in this case because of React. Besides, the fact that RecyclerView overrides onAttachedToWindow and ScrollView does not, indicates that there is already a strong difference in the behavior of these two views.
  4. RecyclerViewBackedScrollView extends RecyclerView, so in order to fix the issue, we must override again onAttachedToWindow and call onLayout immediately after super.onAttachedToWindow().
  5. The fix does not add any "strange" behavior to the RecyclerViewBackedScrollView: it just reminds the RecyclerViewBackedScrollView that the layout pass had already happened.
  6. The real question is: why the issue does not occur when the RecyclerViewBackedScrollView is added to a simple ViewGroup and instead it occurs when it's added to a ViewPager? The answer is that the ViewPager adds new views through an Adapter, not immediately as a normal ViewGroup. So in this case, the RecyclerViewBackedScrollView is layout by React before being added to the view hierarchy.

I hope to have been able to explain in detail why this fix is the best fix currently :)

@mkonicek
Copy link
Contributor

mkonicek commented Nov 23, 2016

Why do you use the RecyclerViewBackedScrollView? We don't use it internally anymore and I was thinking of deleting the code, see #11095.

@godness84
Copy link
Author

@mkonicek I used RecyclerViewBackedScrollView because at that time (7 months ago) ScrollView had some issues and it was less performant.

So ok, let's kill it :)

@lacker
Copy link
Contributor

lacker commented Dec 15, 2016

At this point it seems like the right way to go is for Martin to just kill the RecyclerViewBackedScrollView so I am going to close this pull request. Thanks for bringing this issue up!

@lacker lacker closed this Dec 15, 2016
@facebook-github-bot
Copy link
Contributor

Something went wrong executing that command, @mkonicek could you take a look?

@facebook-github-bot
Copy link
Contributor

Something went wrong executing that command, @mkonicek could you take a look?

@facebook-github-bot
Copy link
Contributor

Something went wrong executing that command, @mkonicek could you take a look?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants