in Libraries/Lists/VirtualizedSectionList.js [289:556]
leadingItem: getItem(sectionData, itemIndex - 1),
leadingSection: sections[i - 1],
trailingItem: getItem(sectionData, itemIndex + 1),
trailingSection: sections[i + 1],
};
}
}
}
_convertViewable = (viewable: ViewToken): ?ViewToken => {
invariant(viewable.index != null, 'Received a broken ViewToken');
const info = this._subExtractor(viewable.index);
if (!info) {
return null;
}
const keyExtractorWithNullableIndex = info.section.keyExtractor;
const keyExtractorWithNonNullableIndex =
this.props.keyExtractor || defaultKeyExtractor;
const key =
keyExtractorWithNullableIndex != null
? keyExtractorWithNullableIndex(viewable.item, info.index)
: keyExtractorWithNonNullableIndex(viewable.item, info.index ?? 0);
return {
...viewable,
index: info.index,
key,
section: info.section,
};
};
_onViewableItemsChanged = ({
viewableItems,
changed,
}: {
viewableItems: Array<ViewToken>,
changed: Array<ViewToken>,
...
}) => {
const onViewableItemsChanged = this.props.onViewableItemsChanged;
if (onViewableItemsChanged != null) {
onViewableItemsChanged({
viewableItems: viewableItems
.map(this._convertViewable, this)
.filter(Boolean),
changed: changed.map(this._convertViewable, this).filter(Boolean),
});
}
};
_renderItem =
(listItemCount: number) =>
({item, index}: {item: Item, index: number, ...}) => {
const info = this._subExtractor(index);
if (!info) {
return null;
}
const infoIndex = info.index;
if (infoIndex == null) {
const {section} = info;
if (info.header === true) {
const {renderSectionHeader} = this.props;
return renderSectionHeader ? renderSectionHeader({section}) : null;
} else {
const {renderSectionFooter} = this.props;
return renderSectionFooter ? renderSectionFooter({section}) : null;
}
} else {
const renderItem = info.section.renderItem || this.props.renderItem;
const SeparatorComponent = this._getSeparatorComponent(
index,
info,
listItemCount,
);
invariant(renderItem, 'no renderItem!');
return (
<ItemWithSeparator
SeparatorComponent={SeparatorComponent}
LeadingSeparatorComponent={
infoIndex === 0 ? this.props.SectionSeparatorComponent : undefined
}
cellKey={info.key}
index={infoIndex}
item={item}
leadingItem={info.leadingItem}
leadingSection={info.leadingSection}
prevCellKey={(this._subExtractor(index - 1) || {}).key}
// Callback to provide updateHighlight for this item
setSelfHighlightCallback={this._setUpdateHighlightFor}
setSelfUpdatePropsCallback={this._setUpdatePropsFor}
// Provide child ability to set highlight/updateProps for previous item using prevCellKey
updateHighlightFor={this._updateHighlightFor}
updatePropsFor={this._updatePropsFor}
renderItem={renderItem}
section={info.section}
trailingItem={info.trailingItem}
trailingSection={info.trailingSection}
inverted={!!this.props.inverted}
/>
);
}
};
_updatePropsFor = (cellKey, value) => {
const updateProps = this._updatePropsMap[cellKey];
if (updateProps != null) {
updateProps(value);
}
};
_updateHighlightFor = (cellKey, value) => {
const updateHighlight = this._updateHighlightMap[cellKey];
if (updateHighlight != null) {
updateHighlight(value);
}
};
_setUpdateHighlightFor = (cellKey, updateHighlightFn) => {
if (updateHighlightFn != null) {
this._updateHighlightMap[cellKey] = updateHighlightFn;
} else {
delete this._updateHighlightFor[cellKey];
}
};
_setUpdatePropsFor = (cellKey, updatePropsFn) => {
if (updatePropsFn != null) {
this._updatePropsMap[cellKey] = updatePropsFn;
} else {
delete this._updatePropsMap[cellKey];
}
};
_getSeparatorComponent(
index: number,
info?: ?Object,
listItemCount: number,
): ?React.ComponentType<any> {
info = info || this._subExtractor(index);
if (!info) {
return null;
}
const ItemSeparatorComponent =
info.section.ItemSeparatorComponent || this.props.ItemSeparatorComponent;
const {SectionSeparatorComponent} = this.props;
const isLastItemInList = index === listItemCount - 1;
const isLastItemInSection =
info.index === this.props.getItemCount(info.section.data) - 1;
if (SectionSeparatorComponent && isLastItemInSection) {
return SectionSeparatorComponent;
}
if (ItemSeparatorComponent && !isLastItemInSection && !isLastItemInList) {
return ItemSeparatorComponent;
}
return null;
}
_updateHighlightMap = {};
_updatePropsMap = {};
_listRef: ?React.ElementRef<typeof VirtualizedList>;
_captureRef = ref => {
this._listRef = ref;
};
}
type ItemWithSeparatorCommonProps = $ReadOnly<{|
leadingItem: ?Item,
leadingSection: ?Object,
section: Object,
trailingItem: ?Item,
trailingSection: ?Object,
|}>;
type ItemWithSeparatorProps = $ReadOnly<{|
...ItemWithSeparatorCommonProps,
LeadingSeparatorComponent: ?React.ComponentType<any>,
SeparatorComponent: ?React.ComponentType<any>,
cellKey: string,
index: number,
item: Item,
setSelfHighlightCallback: (
cellKey: string,
updateFn: ?(boolean) => void,
) => void,
setSelfUpdatePropsCallback: (
cellKey: string,
updateFn: ?(boolean) => void,
) => void,
prevCellKey?: ?string,
updateHighlightFor: (prevCellKey: string, value: boolean) => void,
updatePropsFor: (prevCellKey: string, value: Object) => void,
renderItem: Function,
inverted: boolean,
|}>;
function ItemWithSeparator(props: ItemWithSeparatorProps): React.Node {
const {
LeadingSeparatorComponent,
// this is the trailing separator and is associated with this item
SeparatorComponent,
cellKey,
prevCellKey,
setSelfHighlightCallback,
updateHighlightFor,
setSelfUpdatePropsCallback,
updatePropsFor,
item,
index,
section,
inverted,
} = props;
const [leadingSeparatorHiglighted, setLeadingSeparatorHighlighted] =
React.useState(false);
const [separatorHighlighted, setSeparatorHighlighted] = React.useState(false);
const [leadingSeparatorProps, setLeadingSeparatorProps] = React.useState({
leadingItem: props.leadingItem,
leadingSection: props.leadingSection,
section: props.section,
trailingItem: props.item,
trailingSection: props.trailingSection,
});
const [separatorProps, setSeparatorProps] = React.useState({
leadingItem: props.item,
leadingSection: props.leadingSection,
section: props.section,
trailingItem: props.trailingItem,
trailingSection: props.trailingSection,
});
React.useEffect(() => {
setSelfHighlightCallback(cellKey, setSeparatorHighlighted);
setSelfUpdatePropsCallback(cellKey, setSeparatorProps);
return () => {
setSelfUpdatePropsCallback(cellKey, null);
setSelfHighlightCallback(cellKey, null);
};
}, [
cellKey,
setSelfHighlightCallback,
setSeparatorProps,
setSelfUpdatePropsCallback,
]);
const separators = {
highlight: () => {
setLeadingSeparatorHighlighted(true);
setSeparatorHighlighted(true);
if (prevCellKey != null) {
updateHighlightFor(prevCellKey, true);
}
},
unhighlight: () => {
setLeadingSeparatorHighlighted(false);
setSeparatorHighlighted(false);
if (prevCellKey != null) {
updateHighlightFor(prevCellKey, false);
}
},
updateProps: (
select: 'leading' | 'trailing',
newProps: $Shape<ItemWithSeparatorCommonProps>,
) => {
if (select === 'leading') {
if (LeadingSeparatorComponent != null) {