Skip to content

Instantly share code, notes, and snippets.

@RaphBlanchet
Last active November 11, 2024 13:59
Show Gist options
  • Save RaphBlanchet/472ed013e05398c083caae6216b598b5 to your computer and use it in GitHub Desktop.
Save RaphBlanchet/472ed013e05398c083caae6216b598b5 to your computer and use it in GitHub Desktop.
React-Native SectionList item layout calculations
/* -----------------------
* One thing that is not really sepcified in React-Native's documentation
* of SectionList is what should be provided in getItemLayout function.
* In fact, this function is called for **every** items in your list,
* including the sections' header and footer EVEN IF YOU DON'T PROVIDE
* ANY COMPONENTS FOR THESE ITEMS. This is really important, otherwise you
* would have an offset in your items layout and what's displayed!
* This doesn't apply to "ItemSeparatorComponent", however.
* ----------------------- */
/* -----------------------
* Layout calculation is made only once, when the data changes.
* This helps improving performance by doing the calculations only once
* ----------------------- */
const itemsLayout = useMemo(() => {
if (!data) return [];
const layouts: Array<ListItemLayout> = [];
let index = 0;
let offset = 0;
data.forEach(section => {
// Section header
const TOTAL_HEADER_HEIGHT = SECTION_HEADER_HEIGHT + ITEM_SEPARATOR_HEIGHT * 2;
layouts[index] = { length: TOTAL_HEADER_HEIGHT, offset, index };
index++;
offset += TOTAL_HEADER_HEIGHT;
// Section items
section.data.forEach((_match, i) => {
/* -----------------------
* We need to remove the last ItemSeparator from our calculations otherwise
* we might offset our layout a little. As per the documentation, a ItemSeparatorComponent is
* "Rendered in between adjacent Items within each section."
* ----------------------- */
const MATCH_HEIGHT =
COMPACT_MATCH_CARD_HEIGHT + (i === section.data.length - 1 ? 0 : ITEM_SEPARATOR_HEIGHT);
layouts[index] = {
length: MATCH_HEIGHT,
offset,
index,
};
index++;
offset += MATCH_HEIGHT;
});
// Section footer
layouts[index] = { length: 0, offset, index };
index++;
});
return layouts;
}, [data]);
// And then my list looks something like this
<SectionList
sections={data}
stickySectionHeadersEnabled
initialScrollIndex={initialScrollIndex}
renderItem={({ item }) => <Match match={item} compact />}
getItemLayout={(d, index) => itemsLayout[index] ?? { length: 0, offset: 0, index }}
renderSectionHeader={({ section: { title } }) => <Header title={title} style={{ marginVertical: ITEM_SEPARATOR_HEIGHT }} />}
ItemSeparatorComponent={() => <View height={ITEM_SEPARATOR_HEIGHT} />}
/>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment