debug overlay

Reviewed By: yungsters

Differential Revision: D4534822

fbshipit-source-id: ceef5bc5c0dc8cdc0d3927e198273b4411045bde
This commit is contained in:
Spencer Ahrens 2017-02-13 12:53:59 -08:00 committed by Facebook Github Bot
parent 12b228c5d9
commit 0a86c1cb15
2 changed files with 75 additions and 4 deletions

View File

@ -52,6 +52,7 @@ class FlatListExample extends React.PureComponent {
state = {
data: genItemData(1000),
debug: false,
horizontal: false,
filterText: '',
fixedHeight: true,
@ -90,6 +91,7 @@ class FlatListExample extends React.PureComponent {
{renderSmallSwitchOption(this, 'horizontal')}
{renderSmallSwitchOption(this, 'fixedHeight')}
{renderSmallSwitchOption(this, 'logViewable')}
{renderSmallSwitchOption(this, 'debug')}
</View>
</View>
<FlatList
@ -98,6 +100,7 @@ class FlatListExample extends React.PureComponent {
ItemComponent={this._renderItemComponent}
SeparatorComponent={SeparatorComponent}
data={filteredData}
debug={this.state.debug}
disableVirtualization={!this.state.virtualized}
getItemLayout={this.state.fixedHeight ? this._getItemLayout : undefined}
horizontal={this.state.horizontal}
@ -105,9 +108,9 @@ class FlatListExample extends React.PureComponent {
legacyImplementation={false}
numColumns={1}
onRefresh={() => alert('onRefresh: nothing to refresh :P')}
refreshing={false}
onViewableItemsChanged={this._onViewableItemsChanged}
ref={this._captureRef}
refreshing={false}
shouldItemUpdate={this._shouldItemUpdate}
/>
</UIExplorerPage>

View File

@ -74,6 +74,11 @@ type OptionalProps = {
FooterComponent?: ?ReactClass<*>,
HeaderComponent?: ?ReactClass<*>,
SeparatorComponent?: ?ReactClass<*>,
/**
* `debug` will turn on extra logging and visual overlays to aid with debugging both usage and
* implementation.
*/
debug?: ?boolean,
/**
* DEPRECATED: Virtualization provides significant performance and memory optimizations, but fully
* unmounts react instances that are outside of the render window. You should only need to disable
@ -272,6 +277,7 @@ class VirtualizedList extends React.PureComponent {
item={item}
key={key}
onLayout={this._onCellLayout}
onUnmount={this._onCellUnmount}
parentProps={this.props}
/>
);
@ -345,7 +351,11 @@ class VirtualizedList extends React.PureComponent {
},
cells,
);
return ret;
if (this.props.debug) {
return <View style={{flex: 1}}>{ret}{this._renderDebugOverlay()}</View>;
} else {
return ret;
}
}
componentDidUpdate() {
@ -375,7 +385,7 @@ class VirtualizedList extends React.PureComponent {
_onCellLayout = (e, cellKey, index) => {
const layout = e.nativeEvent.layout;
const next = {offset: this._selectOffset(layout), length: this._selectLength(layout), index};
const next = {offset: this._selectOffset(layout), length: this._selectLength(layout), index, inLayout: true};
const curr = this._frames[cellKey];
if (!curr ||
next.offset !== curr.offset ||
@ -391,6 +401,13 @@ class VirtualizedList extends React.PureComponent {
}
};
_onCellUnmount = (cellKey: string) => {
const curr = this._frames[cellKey];
if (curr) {
this._frames[cellKey] = {...curr, inLayout: false};
}
};
_onLayout = (e: Object) => {
this._scrollMetrics.visibleLength = this._selectLength(e.nativeEvent.layout);
this.props.onLayout && this.props.onLayout(e);
@ -405,6 +422,53 @@ class VirtualizedList extends React.PureComponent {
this._headerLength = this._selectLength(e.nativeEvent.layout);
};
_renderDebugOverlay() {
const normalize = this._scrollMetrics.visibleLength / this._scrollMetrics.contentLength;
const framesInLayout = [];
const itemCount = this.props.getItemCount(this.props.data);
for (let ii = 0; ii < itemCount; ii++) {
const frame = this._getFrameMetricsApprox(ii);
if (frame.inLayout) {
framesInLayout.push(frame);
}
}
const windowTop = this._getFrameMetricsApprox(this.state.first).offset;
const frameLast = this._getFrameMetricsApprox(this.state.last);
const windowLen = frameLast.offset + frameLast.length - windowTop;
const visTop = this._scrollMetrics.offset;
const visLen = this._scrollMetrics.visibleLength;
const baseStyle = {position: 'absolute', top: 0, right: 0};
return (
<View style={{...baseStyle, bottom: 0, width: 20, borderColor: 'blue', borderWidth: 1}}>
{framesInLayout.map((f, ii) =>
<View key={'f' + ii} style={{
...baseStyle,
left: 0,
top: f.offset * normalize,
height: f.length * normalize,
backgroundColor: 'orange',
}} />
)}
<View style={{
...baseStyle,
left: 0,
top: windowTop * normalize,
height: windowLen * normalize,
borderColor: 'green',
borderWidth: 2,
}} />
<View style={{
...baseStyle,
left: 0,
top: visTop * normalize,
height: visLen * normalize,
borderColor: 'red',
borderWidth: 2,
}} />
</View>
);
}
_selectLength(metrics: {height: number, width: number}): number {
return !this.props.horizontal ? metrics.height : metrics.width;
}
@ -572,6 +636,7 @@ class CellRenderer extends React.Component {
index: number,
item: Item,
onLayout: (event: Object, cellKey: string, index: number) => void,
onUnmount: (cellKey: string) => void,
parentProps: {
ItemComponent: ItemComponentType,
getItemLayout?: ?Function,
@ -584,6 +649,9 @@ class CellRenderer extends React.Component {
_onLayout = (e) => {
this.props.onLayout(e, this.props.cellKey, this.props.index);
}
componentWillUnmount() {
this.props.onUnmount(this.props.cellKey);
}
shouldComponentUpdate(nextProps, nextState) {
const curr = {item: this.props.item, index: this.props.index};
const next = {item: nextProps.item, index: nextProps.index};
@ -593,7 +661,7 @@ class CellRenderer extends React.Component {
const {item, index, parentProps} = this.props;
const {ItemComponent, getItemLayout} = parentProps;
const element = <ItemComponent item={item} index={index} />;
if (getItemLayout) {
if (getItemLayout && !parentProps.debug) {
return element;
}
return (