Bibs
Bibs

Reputation: 1015

Which SectionHeader is Sticky in react-native?

I'm using React-Native's SectionList component to implement a list with sticky section headers. I'd like to apply special styling to whichever section header is currently sticky.

I've tried 2 methods to determine which sticky header is currently "sticky," and neither have worked:

  1. I tried to use each section header's onLayout prop to determine each of their y offsets, and use that in combination with the SectionList onScroll event to calculate which section header is currently "sticky".
  2. I tried to use SectionList's onViewableItemsChanged prop to figure out which header is currently sticky.

Approach #1 doesn't work because the event object passed to the onLayout callback only contains the nativeEvent height and width properties. Approach #2 doesn't work because the onViewableItemsChanged callback appears to be invoked with inconsistent data (initially, the first section header is marked as viewable (which it is), then, once it becomes "sticky," it is marked as not viewable, and then with further scrolling it is inexplicable marked as viewable again while it is still "sticky" [this final update seems completely arbitrary]).

Anyone know a solution that works?

Upvotes: 5

Views: 1573

Answers (1)

Serhii Harbovskyi
Serhii Harbovskyi

Reputation: 318

While you creating stickyHeaderIndices array in render() method you should create an object first. Where key is index and value is offset of that particular row, eg. { 0: 0, 5: 100, 10: 200 }

  constructor(){
    super(props);
    this.headersRef = {};
    this.stickyHeadersObject = {};
    this.stickyHeaderVisible = {};
  }

  createHeadersObject = (data) => {
    const obj = {};
    for (let i = 0, l = data.length; i < l; i++) {
      const row = data[i];
      if (row.isHeaderRow) {
        obj[i] = row.offset;
      }
    }
    return obj;
  };

  createHeadersArray = data => Object.keys(data).map(str => parseInt(str, 10))

  render() {
    const { data } = this.props;
    // Expected that data array already has offset:number and isHeaderRow:boolean values
    this.stickyHeadersObject = this.createHeadersObject(data);
    const stickyIndicesArray = this.createHeadersArray(this.stickyIndicesObject);
    const stickyHeaderIndices = { stickyHeaderIndices: stickyIndicesArray };
  return (<FlatList
    ...
    onScroll={event => this.onScroll(event.nativeEvent.contentOffset.y)}

    {...stickyHeaderIndices}
    ...
    renderItem={({ item, index }) => {
            return (
                { // render header row
                  item.isHeaderRow &&
                      <HeaderRow
                        ref={ref => this.headersRef[index] = ref}
                      />
                }
                { // render regular row
                  item.isHeaderRow &&
                     <RegularRow />
                }
            );
          }}
  />)

Then you have to monitor is current offset bigger than your "titleRow"

  onScroll = (offset) => {
    Object.keys(this.stickyHeadersObject).map((key) => {
     this.stickyHeaderVisible[key] = this.stickyHeadersObject[key] <= offset;
     return this.headersRef[key] && this.headersRef[key].methodToUpdateStateInParticularHeaderRow(this.stickyHeaderVisible[key]);
    });
  };

Upvotes: 1

Related Questions