Recyclerlistview: How to define dynamic LayoutProvider

Created on 28 May 2018  路  4Comments  路  Source: Flipkart/recyclerlistview

I am trying to define layout provider where type is basically coming in data source, so how can i define layout provider to take layout type when data source is updated. Sample code of my requirement is:
this.layoutProvider = new LayoutProvider( index => { return this.getDataSource()[index].widgetType }, (type, dim) => { dim.width = width; dim.height = height; } );

Most helpful comment

@nehacurefit when you receive new Data, you can access it inside componentWillReceiveProps (or getDerivedPropsFromState). After - you can setup new layout provider, and after you can setup new data provider. Is it works for you?

const DataSourceFactory = () =>
  new DataProvider((r1, r2) => {
    return r1 !== r2;
  });

const LayoutProviderFactory = results => {
  return new LayoutProvider(
    index => {
      const searchItem = results[index];
      if (!searchItem) {
        return 'unknown';
      }
      return searchItem.layoutType;
    },
    (type: SearchItemLayout, dim) => {
      switch (type) {
        case SEARCH_ITEM_LAYOUT_TYPES.Cover:
          dim.width = SEARCH_SIDEBAR_WIDTH;
          dim.height = SEARCH_ITEM_COVER_HEIGHT;
          break;
        case SEARCH_ITEM_LAYOUT_TYPES.Match:
          dim.width = SEARCH_SIDEBAR_WIDTH;
          dim.height = SEARCH_ITEM_HEIGHT;
          break;
        default:
          dim.width = 0;
          dim.height = 0;
      }
    }
  );
};
...
class ResultList extends React.Component<Props, State> {
  _layoutProvider: LayoutProvider;
  _dataProvider: DataProvider;

  constructor(props: Props) {
    super(props);

    const searchResults = this.props.preparedSearchResults;
    this._dataProvider = DataSourceFactory();
    this._layoutProvider = LayoutProviderFactory(searchResults);

    this.state = {
      dataProvider: this._dataProvider.cloneWithRows(searchResults),
    };
  }
...
...
componentWillReceiveProps(nextProps) {
    if (this.props.preparedSearchResults !== nextProps.preparedSearchResults) {
      const searchResults = nextProps.preparedSearchResults;
      this._dataProvider = DataSourceFactory();
      this._layoutProvider = LayoutProviderFactory(searchResults);

      this.setState(prev => ({
        ...prev,
        dataProvider: this._dataProvider.cloneWithRows(searchResults),
      }));
    }
  }
...
render() {
    const {
      initialRenderIndex,
      preparedSearchResults,
    } = this.props;

    return (
      <View style={styles.root}>
        <RecyclerListView
          canChangeSize
          layoutProvider={this._layoutProvider}
          dataProvider={this.state.dataProvider}
          rowRenderer={this._rowRenderer}
          initialRenderIndex={initialRenderIndex}
        />
      </View>
    );
  }

@naqvitalha is it correct behaviour? (because I have some problems here, related to my previous bug with initialRenderIndex)

All 4 comments

@nehacurefit when you receive new Data, you can access it inside componentWillReceiveProps (or getDerivedPropsFromState). After - you can setup new layout provider, and after you can setup new data provider. Is it works for you?

const DataSourceFactory = () =>
  new DataProvider((r1, r2) => {
    return r1 !== r2;
  });

const LayoutProviderFactory = results => {
  return new LayoutProvider(
    index => {
      const searchItem = results[index];
      if (!searchItem) {
        return 'unknown';
      }
      return searchItem.layoutType;
    },
    (type: SearchItemLayout, dim) => {
      switch (type) {
        case SEARCH_ITEM_LAYOUT_TYPES.Cover:
          dim.width = SEARCH_SIDEBAR_WIDTH;
          dim.height = SEARCH_ITEM_COVER_HEIGHT;
          break;
        case SEARCH_ITEM_LAYOUT_TYPES.Match:
          dim.width = SEARCH_SIDEBAR_WIDTH;
          dim.height = SEARCH_ITEM_HEIGHT;
          break;
        default:
          dim.width = 0;
          dim.height = 0;
      }
    }
  );
};
...
class ResultList extends React.Component<Props, State> {
  _layoutProvider: LayoutProvider;
  _dataProvider: DataProvider;

  constructor(props: Props) {
    super(props);

    const searchResults = this.props.preparedSearchResults;
    this._dataProvider = DataSourceFactory();
    this._layoutProvider = LayoutProviderFactory(searchResults);

    this.state = {
      dataProvider: this._dataProvider.cloneWithRows(searchResults),
    };
  }
...
...
componentWillReceiveProps(nextProps) {
    if (this.props.preparedSearchResults !== nextProps.preparedSearchResults) {
      const searchResults = nextProps.preparedSearchResults;
      this._dataProvider = DataSourceFactory();
      this._layoutProvider = LayoutProviderFactory(searchResults);

      this.setState(prev => ({
        ...prev,
        dataProvider: this._dataProvider.cloneWithRows(searchResults),
      }));
    }
  }
...
render() {
    const {
      initialRenderIndex,
      preparedSearchResults,
    } = this.props;

    return (
      <View style={styles.root}>
        <RecyclerListView
          canChangeSize
          layoutProvider={this._layoutProvider}
          dataProvider={this.state.dataProvider}
          rowRenderer={this._rowRenderer}
          initialRenderIndex={initialRenderIndex}
        />
      </View>
    );
  }

@naqvitalha is it correct behaviour? (because I have some problems here, related to my previous bug with initialRenderIndex)

Thanks @maxfarseer for help

@maxfarseer THANK YOU SO MUCH FOR THIS Example!!!! I know this is from 2018 but this just helped me solve a problem that I've been wracking my brain over.. You're the man! 馃 馃榿 馃憤馃徑

@jdpagley happy new 2021 馃懐

Was this page helpful?
0 / 5 - 0 ratings

Related issues

h-asadollahi picture h-asadollahi  路  8Comments

gilra2000 picture gilra2000  路  6Comments

LukaszK88 picture LukaszK88  路  6Comments

H-Shafiei picture H-Shafiei  路  8Comments

Komeyl94 picture Komeyl94  路  9Comments