Flipkart / recyclerlistview

High performance listview for React Native and web!
Apache License 2.0
5.25k stars 428 forks source link

forceNonDeterministicRendering not working as expected #715

Open Alex1899 opened 2 years ago

Alex1899 commented 2 years ago

Hi,

I am trying to implement a facebook style feed page in the app. So, I have two layout types: 1) post with media and 2) post with just text without media.

As I don't know the exact height of text the user writes, I want to use the forceNonDeterministicRendering prop with canChangeSize. However, whenever I enable forceNonDeterministicRendering prop, the contents of posts disappear and I am not sure why. You can see this behaviour below:

Here is my code:

CustomRecyclerList component:

import {Center, Column, Divider, Spinner} from 'native-base';
import React, {ComponentType} from 'react';
import {Dimensions, RefreshControl} from 'react-native';
import {DataProvider, LayoutProvider, RecyclerListView} from 'recyclerlistview';
import {Post} from '~types/post/Post';

const ViewTypes = {
  POST_NO_MEDIA: 0,
  POST_WITH_MEDIA: 1,
};

const PostSizes = {
  POST_CONTENT: 300,
  POST_HEADER: 60,
  POST_WITH_MEDIA: 450,
  POST_NO_MEDIA: 100,
};

interface CustomRecyclerListProps {
  getData: (lastDoc?: string) => Promise<{data: Post[]; hasMore: boolean}>;
  RowItem: ComponentType<{item: Post}>;
  disableRefreshControl?: boolean;
}
export default class CustomRecyclerList extends React.Component<
  CustomRecyclerListProps,
  {
    dataProvider: DataProvider;
    posts: Post[];
    hasMore: boolean;
    refreshing: boolean;
  }
> {
  private _layoutProvider: LayoutProvider;

  constructor(args: any) {
    super(args);

    this.state = {
      dataProvider: new DataProvider((r1, r2) => {
        return r1.id !== r2.id;
      }),

      hasMore: true,
      posts: [],
      refreshing: true,
    };

    let {width} = Dimensions.get('window');

    this._layoutProvider = new LayoutProvider(
      index => {
        let media = this.state.dataProvider.getDataForIndex(index)?.media;
        if (media?.length === 0) {
          return ViewTypes.POST_NO_MEDIA;
        } else {
          return ViewTypes.POST_WITH_MEDIA;
        }
      },
      (type, dim) => {
        switch (type) {
          case ViewTypes.POST_NO_MEDIA:
            dim.width = width;
            dim.height = PostSizes.POST_NO_MEDIA;
            break;
          case ViewTypes.POST_WITH_MEDIA:
            dim.width = width;
            dim.height = PostSizes.POST_WITH_MEDIA;
            break;
          default:
            dim.width = 0;
            dim.height = 0;
        }
      },
    );
    this._rowRenderer = this._rowRenderer.bind(this);
    this._renderFooter = this._renderFooter.bind(this);
    this.onEndReachedCallback = this.onEndReachedCallback.bind(this);
    this.onRefreshCallback = this.onRefreshCallback.bind(this);
  }

  componentDidMount() {
    this.onRefreshCallback();
  }
  onRefreshCallback() {
    console.log('calling refresh ');
    this.setState({...this.state, refreshing: true});
    this.props
      .getData()
      .then(async ({data: items, hasMore}) => {
        this.setState({
          dataProvider: this.state.dataProvider.cloneWithRows(items),
          hasMore,
          posts: items,
          refreshing: false,
        });
      })
      .catch(e => {
        console.log(e.message);
        this.setState({...this.state, refreshing: false});
      });
  }

  onEndReachedCallback() {
    if (!this.state.hasMore) {
      return;
    }
    let dataSize = this.state.dataProvider.getSize();
    if (dataSize === 0) {
      return;
    }
    let lastDocId: string = this.state.dataProvider.getDataForIndex(
      dataSize - 1,
    ).id;
    console.log('calling end reached with lastDocId: ', lastDocId);
    this.props
      .getData(lastDocId)
      .then(async ({data: items, hasMore}) => {
        this.setState({
          dataProvider: this.state.dataProvider.cloneWithRows(
            this.state.posts.concat(items),
          ),
          hasMore,
          posts: this.state.posts.concat(items),
          refreshing: false,
        });
      })
      .catch(e => {
        console.log(e.message);
        this.setState({...this.state, refreshing: false});
      });
  }

  _rowRenderer(type: any, data: Post) {
    let RowItem = this.props.RowItem;
    return (
      <React.Fragment>
        <RowItem item={data} />
        <Divider h={0} my={1} w="100%" />
      </React.Fragment>
    );
  }

  _renderFooter() {
    return (
      <Center pt={9} pb={this.state.hasMore ? 32 : 20}>
        {this.state.hasMore && <Spinner size={40} color="blue" />}
      </Center>
    );
  }
  render() {
    return (
      <Column w={'100%'} flex={1} minH={1} minW={1}>
        {this.state.dataProvider.getSize() === 0 ? (
          <Center>
            <Spinner color="blue" />
          </Center>
        ) : (
          <RecyclerListView
            layoutProvider={this._layoutProvider}
            dataProvider={this.state.dataProvider}
            style={{flex: 1}}
            rowRenderer={this._rowRenderer}
            renderAheadOffset={200}
            onEndReachedThreshold={50}
            onEndReached={this.onEndReachedCallback}
            renderFooter={this._renderFooter}
            canChangeSize={true}
            forceNonDeterministicRendering={true}
            scrollViewProps={{
              refreshControl: (
                <RefreshControl
                  refreshing={this.state.refreshing}
                  onRefresh={this.onRefreshCallback}
                />
              ),
            }}
          />
        )}
      </Column>
    );
  }
}
mohity777 commented 2 years ago

Hi did u get any way ??

Alex1899 commented 2 years ago

Hi did u get any way ??

Unfortunately no. I moved on to using shopify flashlist library.

mohity777 commented 2 years ago

Does that support Staggered View/ masonry view or dynamic heights?

martymfly commented 2 years ago

Does that support Staggered View/ masonry view or dynamic heights?

Flashlist doesn't support dynamic heights and estimatedItemSize prop also doesn't work well with dynamic heights..