Add a prop to toggle ListView sticky section headers

Summary:
Hello,

This PR adds a property to the `ListView` to enable and disable to sticky sections headers behaviour. Current this is enabled by default and there is no way to disable it. It has been previously discussed in #1974 where there was a suggestion to add the `ListView` inside `ScrollView`. This is bad for performance, but some people were using that as a workaround. satya164 suggested someone submitting a PR, which is why I'm here 😉

I have tested the property manually by adding `stickySectionHeaders={false}` to the `<ListView> Paging` example in `UIExplorer`. I have also tested that the current behaviour still stands, so this is a non-breaking change.

I have also checked that the website displays the new documentation.

I couldn't see anywhere to add automated tests to this, but if there is feel free to point it out and I'll update this PR.

I tried running `npm run lint` to check the code style, but it spat out loads and loads of errors. I presume I have something set up incorrectly. Feel
Closes https://github.com/facebook/react-native/pull/11700

Differential Revision: D4380916

fbshipit-source-id: 7782043afc9f0108c81f97952fed60b153479cac
This commit is contained in:
Matt Oakes 2017-01-09 03:08:05 -08:00 committed by Facebook Github Bot
parent 5fdd6b33fa
commit 55427566b6
1 changed files with 24 additions and 19 deletions

View File

@ -56,7 +56,7 @@ var DEFAULT_SCROLL_CALLBACK_THROTTLE = 50;
/**
* ListView - A core component designed for efficient display of vertically
* scrolling lists of changing data. The minimal API is to create a
* scrolling lists of changing data. The minimal API is to create a
* [`ListView.DataSource`](docs/listviewdatasource.html), populate it with a simple
* array of data blobs, and instantiate a `ListView` component with that data
* source and a `renderRow` callback which takes a blob from the data array and
@ -100,7 +100,7 @@ var DEFAULT_SCROLL_CALLBACK_THROTTLE = 50;
* source data has changed - see ListViewDataSource for more details.
*
* * Rate-limited row rendering - By default, only one row is rendered per
* event-loop (customizable with the `pageSize` prop). This breaks up the
* event-loop (customizable with the `pageSize` prop). This breaks up the
* work into smaller chunks to reduce the chance of dropping frames while
* rendering rows.
*/
@ -145,7 +145,7 @@ var ListView = React.createClass({
* (rowData, sectionID, rowID, highlightRow) => renderable
*
* Takes a data entry from the data source and its ids and should return
* a renderable component to be rendered as the row. By default the data
* a renderable component to be rendered as the row. By default the data
* is exactly what was put into the data source, but it's also possible to
* provide custom extractors. ListView can be notified when a row is
* being highlighted by calling `highlightRow(sectionID, rowID)`. This
@ -155,14 +155,14 @@ var ListView = React.createClass({
*/
renderRow: PropTypes.func.isRequired,
/**
* How many rows to render on initial component mount. Use this to make
* How many rows to render on initial component mount. Use this to make
* it so that the first screen worth of data appears at one time instead of
* over the course of multiple frames.
*/
initialListSize: PropTypes.number.isRequired,
/**
* Called when all rows have been rendered and the list has been scrolled
* to within onEndReachedThreshold of the bottom. The native scroll
* to within onEndReachedThreshold of the bottom. The native scroll
* event is provided.
*/
onEndReached: PropTypes.func,
@ -182,8 +182,8 @@ var ListView = React.createClass({
* () => renderable
*
* The header and footer are always rendered (if these props are provided)
* on every render pass. If they are expensive to re-render, wrap them
* in StaticContainer or other mechanism as appropriate. Footer is always
* on every render pass. If they are expensive to re-render, wrap them
* in StaticContainer or other mechanism as appropriate. Footer is always
* at the bottom of the list, and header at the top, on every render pass.
*/
renderFooter: PropTypes.func,
@ -191,13 +191,7 @@ var ListView = React.createClass({
/**
* (sectionData, sectionID) => renderable
*
* If provided, a sticky header is rendered for this section. The sticky
* behavior means that it will scroll with the content at the top of the
* section until it reaches the top of the screen, at which point it will
* stick to the top until it is pushed off the screen by the next section
* header.
*
* NOTE: On Android, the header is not sticky.
* If provided, a header is rendered for this section.
*/
renderSectionHeader: PropTypes.func,
/**
@ -215,7 +209,7 @@ var ListView = React.createClass({
/**
* (visibleRows, changedRows) => void
*
* Called when the set of visible rows changes. `visibleRows` maps
* Called when the set of visible rows changes. `visibleRows` maps
* { sectionID: { rowID: true }} for all the visible rows, and
* `changedRows` maps { sectionID: { rowID: true | false }} for the rows
* that have changed their visibility, with true indicating visible, and
@ -225,9 +219,17 @@ var ListView = React.createClass({
/**
* A performance optimization for improving scroll perf of
* large lists, used in conjunction with overflow: 'hidden' on the row
* containers. This is enabled by default.
* containers. This is enabled by default.
*/
removeClippedSubviews: React.PropTypes.bool,
/**
* Makes the sections headers sticky. The sticky behavior means that it
* will scroll with the content at the top of the section until it reaches
* the top of the screen, at which point it will stick to the top until it
* is pushed off the screen by the next section header.
* @platform ios
*/
stickySectionHeadersEnabled: React.PropTypes.bool,
/**
* An array of child indices determining which children get docked to the
* top of the screen when scrolling. For example, passing
@ -304,6 +306,7 @@ var ListView = React.createClass({
renderScrollComponent: props => <ScrollView {...props} />,
scrollRenderAheadDistance: DEFAULT_SCROLL_RENDER_AHEAD,
onEndReachedThreshold: DEFAULT_END_REACHED_THRESHOLD,
stickySectionHeadersEnabled: true,
stickyHeaderIndices: [],
};
},
@ -374,7 +377,7 @@ var ListView = React.createClass({
var dataSource = this.props.dataSource;
var allRowIDs = dataSource.rowIdentities;
var rowCount = 0;
var sectionHeaderIndices = [];
var stickySectionHeaderIndices = [];
var header = this.props.renderHeader && this.props.renderHeader();
var footer = this.props.renderFooter && this.props.renderFooter();
@ -413,7 +416,9 @@ var ListView = React.createClass({
)}
/>
);
sectionHeaderIndices.push(totalIndex++);
if (this.props.stickySectionHeadersEnabled) {
stickySectionHeaderIndices.push(totalIndex++);
}
}
for (var rowIdx = 0; rowIdx < rowIDs.length; rowIdx++) {
@ -474,7 +479,7 @@ var ListView = React.createClass({
}
Object.assign(props, {
onScroll: this._onScroll,
stickyHeaderIndices: this.props.stickyHeaderIndices.concat(sectionHeaderIndices),
stickyHeaderIndices: this.props.stickyHeaderIndices.concat(stickySectionHeaderIndices),
// Do not pass these events downstream to ScrollView since they will be
// registered in ListView's own ScrollResponder.Mixin