react-infinite icon indicating copy to clipboard operation
react-infinite copied to clipboard

Infinite not working ok when resizing having dynamic heights

Open matiasgarcia opened this issue 7 years ago • 6 comments

First of all I must say ReactInfinite is great. It's simple and solves a complex problem.

I am having an issue when trying to render a list with dynamic heights. I use ReactMeasure to get the proper height.

I noticed that if I resize the window, the elements from the screen dissapear prematurely. I am not sure if I am using the library wrongly or if I should change some of the state/props given to the component when resizing occurs.

Here is a gif of the issue

const PostList = React.createClass({
  mixins: [React.addons.PureRenderMixin],

  getInitialState(){
    return {
      elementHeights: {}
    }
  },

  loadingSpinnerDelegate(){
    return <LoadingSpinner/>
  },

  handleResize( postId, index, { bounds, entry }){
    const height = bounds.height;

    this.setState((prevState, props) => ({
      elementHeights: {
        ...prevState.elementHeights,
        [postId]: height
      }
    }));
  },

  getElementHeight(){
    return _.map(this.props.posts, (post) => this.state.elementHeights[post.id] || 300)
  },

  render(){
    const { posts, pollFeature, loading, fetchNext } = this.props;
    if(posts.length === 0){
      return null;
    }
    return (
      <Infinite
        loadingSpinnerDelegate={this.loadingSpinnerDelegate()}
        useWindowAsScrollContainer
        elementHeight={this.getElementHeight()}
        infiniteLoadBeginEdgeOffset={300}
        isInfiniteLoading={loading}
        onInfiniteLoad={fetchNext}
        preloadBatchSize={Infinite.containerHeightScaleFactor(2)}>
        {_.map(posts, (post, index) =>
          <Measure.default key={post.id}
                           onResize={this.handleResize.bind(null, post.id, index)}
                           bounds>
            {({ measureRef, measure, contentRect }) => (
              <div ref={measureRef}>
                <RegularPost key={post.id}
                             pollFeature={pollFeature}
                             post={post}
                             standalone={false}/>
              </div>
            )}
          </Measure.default>
        )}
      </Infinite>
    )
  }
});

PostList.propTypes = PostFeed.propTypes;

matiasgarcia avatar Jul 27 '17 18:07 matiasgarcia

Hmm I haven't looked into this too much, but this might be because we don't recompute the size of the window when it is resized when using useWindowAsScrollContainer, which we should certainly fix.

garetht avatar Jul 27 '17 21:07 garetht

I haven't looked at the implementation yet but I was suspecting that. I will see if I can find the problem in the source code and submit a pull request.

Thanks @garetht

matiasgarcia avatar Jul 27 '17 21:07 matiasgarcia

@garetht Do you know a quick workaround for this? It seems that the containerHeight is not kept as an internal state, so this might be a huge refactor if I am not wrong?

matiasgarcia avatar Jul 27 '17 22:07 matiasgarcia

It might be possible to just modify computedProps directly with a listener on window resize, although that is on the hacky side. this.props is rarely used directly.

garetht avatar Jul 27 '17 23:07 garetht

@garetht I have been working on the """hack""" and the resize sometimes work and sometimes doesn't. I wanted to ask you if you knew where this error could come from.

Basically I added a resize handler that does something similar to what is done in componentWillReceiveProps:

  handleResize(e) {
    var nextInternalState = this.recomputeInternalStateFromProps(this.props, window.innerHeight);

    this.computedProps = nextInternalState.computedProps;
    this.utils = nextInternalState.utils;

    this.setState(nextInternalState.newState);
  },

As you see, the recomputeInternalStateFromProps now receives a height. Which is used and replaced in the generateComputedProps method:

  recomputeInternalStateFromProps(props: ReactInfiniteProps, forcedHeight = null): {
    computedProps: ReactInfiniteComputedProps,
    utils: ReactInfiniteUtilityFunctions,
    newState: ReactInfiniteState
    } {
    checkProps(props);
    var computedProps = this.generateComputedProps(props, forcedHeight);
    ...
  generateComputedProps(props: ReactInfiniteProps, forcedHeight = null): ReactInfiniteComputedProps {
    // These are extracted so their type definitions do not conflict.
    var {containerHeight,
          preloadBatchSize,
          preloadAdditionalHeight,
          ...oldProps} = props;

    var newProps = {};
    containerHeight = typeof containerHeight === 'number' ? containerHeight : 0;
    if (forcedHeight) {
      newProps.containerHeight = forcedHeight;
    } else {
      newProps.containerHeight = props.useWindowAsScrollContainer
        ? window.innerHeight : containerHeight;
    }
    ....

However, depending on the size of the screen elements might dissapear.

peek 28-07-2017 16-33

What else can I check specifically? I am trying to debug the code.

matiasgarcia avatar Jul 28 '17 19:07 matiasgarcia

still working?

geminiyellow avatar Jul 06 '18 06:07 geminiyellow