mirror of
https://github.com/status-im/react-native.git
synced 2025-01-14 03:26:07 +00:00
VirtualizedList: fix bug where onViewableItemsChanged wouldn't trigger
Summary: In the current implementation of the `VirtualizedList` the `onViewableItemsChanged` callback wouldn't trigger if the underlying list data changes. (see example snack https://snack.expo.io/Hk5703eBb) I added a method in the `ViewabilityHelper` to invalidate the cached viewableIndices, which gets triggered when the list-data changes. Closes https://github.com/facebook/react-native/pull/14922 Differential Revision: D5864537 Pulled By: sahrens fbshipit-source-id: 37f617763596244208548817d5b138dadc12c75d
This commit is contained in:
parent
d980632233
commit
6747a36f5d
@ -223,6 +223,13 @@ class ViewabilityHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clean-up cached _viewableIndices to evaluate changed items on next update
|
||||||
|
*/
|
||||||
|
resetViewableIndices() {
|
||||||
|
this._viewableIndices = [];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Records that an interaction has happened even if there has been no scroll.
|
* Records that an interaction has happened even if there has been no scroll.
|
||||||
*/
|
*/
|
||||||
|
@ -516,6 +516,12 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
|||||||
});
|
});
|
||||||
if (data !== this.props.data || extraData !== this.props.extraData) {
|
if (data !== this.props.data || extraData !== this.props.extraData) {
|
||||||
this._hasDataChangedSinceEndReached = true;
|
this._hasDataChangedSinceEndReached = true;
|
||||||
|
|
||||||
|
// clear the viewableIndices cache to also trigger
|
||||||
|
// the onViewableItemsChanged callback with the new data
|
||||||
|
this._viewabilityTuples.forEach(tuple => {
|
||||||
|
tuple.viewabilityHelper.resetViewableIndices();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -385,4 +385,54 @@ describe('onUpdate', function() {
|
|||||||
viewableItems: [{isViewable: true, key: 'a'}],
|
viewableItems: [{isViewable: true, key: 'a'}],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns the right visible row after the underlying data changed', function() {
|
||||||
|
const helper = new ViewabilityHelper();
|
||||||
|
rowFrames = {
|
||||||
|
a: {y: 0, height: 200},
|
||||||
|
b: {y: 200, height: 200},
|
||||||
|
};
|
||||||
|
data = [{key: 'a'}, {key: 'b'}];
|
||||||
|
const onViewableItemsChanged = jest.fn();
|
||||||
|
helper.onUpdate(
|
||||||
|
data.length,
|
||||||
|
0,
|
||||||
|
200,
|
||||||
|
getFrameMetrics,
|
||||||
|
createViewToken,
|
||||||
|
onViewableItemsChanged,
|
||||||
|
);
|
||||||
|
expect(onViewableItemsChanged.mock.calls.length).toBe(1);
|
||||||
|
expect(onViewableItemsChanged.mock.calls[0][0]).toEqual({
|
||||||
|
changed: [{isViewable: true, key: 'a'}],
|
||||||
|
viewabilityConfig: {viewAreaCoveragePercentThreshold: 0},
|
||||||
|
viewableItems: [{isViewable: true, key: 'a'}],
|
||||||
|
});
|
||||||
|
|
||||||
|
// update data
|
||||||
|
rowFrames = {
|
||||||
|
c: {y: 0, height: 200},
|
||||||
|
a: {y: 200, height: 200},
|
||||||
|
b: {y: 400, height: 200},
|
||||||
|
};
|
||||||
|
data = [{key: 'c'}, {key: 'a'}, {key: 'b'}];
|
||||||
|
|
||||||
|
helper.resetViewableIndices();
|
||||||
|
|
||||||
|
helper.onUpdate(
|
||||||
|
data.length,
|
||||||
|
0,
|
||||||
|
200,
|
||||||
|
getFrameMetrics,
|
||||||
|
createViewToken,
|
||||||
|
onViewableItemsChanged,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(onViewableItemsChanged.mock.calls.length).toBe(2);
|
||||||
|
expect(onViewableItemsChanged.mock.calls[1][0]).toEqual({
|
||||||
|
changed: [{isViewable: true, key: 'c'}, {isViewable: false, key: 'a'}],
|
||||||
|
viewabilityConfig: {viewAreaCoveragePercentThreshold: 0},
|
||||||
|
viewableItems: [{isViewable: true, key: 'c'}],
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -160,4 +160,63 @@ describe('VirtualizedList', () => {
|
|||||||
);
|
);
|
||||||
expect(component).toMatchSnapshot();
|
expect(component).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns the viewableItems correctly in the onViewableItemsChanged callback after changing the data', () => {
|
||||||
|
const ITEM_HEIGHT = 800;
|
||||||
|
let data = [{key: 'i1'}, {key: 'i2'}, {key: 'i3'}];
|
||||||
|
const nativeEvent = {
|
||||||
|
contentOffset: {y: 0, x: 0},
|
||||||
|
layoutMeasurement: {width: 300, height: 600},
|
||||||
|
contentSize: {width: 300, height: data.length * ITEM_HEIGHT},
|
||||||
|
zoomScale: 1,
|
||||||
|
contentInset: {right: 0, top: 0, left: 0, bottom: 0},
|
||||||
|
};
|
||||||
|
const onViewableItemsChanged = jest.fn();
|
||||||
|
const props = {
|
||||||
|
data,
|
||||||
|
renderItem: ({item}) => <item value={item.key} />,
|
||||||
|
getItem: (data, index) => data[index],
|
||||||
|
getItemCount: data => data.length,
|
||||||
|
getItemLayout: (data, index) => ({
|
||||||
|
length: ITEM_HEIGHT,
|
||||||
|
offset: ITEM_HEIGHT * index,
|
||||||
|
index,
|
||||||
|
}),
|
||||||
|
onViewableItemsChanged,
|
||||||
|
};
|
||||||
|
|
||||||
|
const component = ReactTestRenderer.create(<VirtualizedList {...props} />);
|
||||||
|
|
||||||
|
const instance = component.getInstance();
|
||||||
|
|
||||||
|
instance._onScrollBeginDrag({nativeEvent});
|
||||||
|
instance._onScroll({
|
||||||
|
timeStamp: 1000,
|
||||||
|
nativeEvent,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(onViewableItemsChanged).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onViewableItemsChanged).toHaveBeenLastCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
viewableItems: [expect.objectContaining({isViewable: true, key: 'i1'})],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
data = [{key: 'i4'}, ...data];
|
||||||
|
component.update(<VirtualizedList {...props} data={data} />);
|
||||||
|
|
||||||
|
instance._onScroll({
|
||||||
|
timeStamp: 2000,
|
||||||
|
nativeEvent: {
|
||||||
|
...nativeEvent,
|
||||||
|
contentOffset: {y: 100, x: 0},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(onViewableItemsChanged).toHaveBeenCalledTimes(2);
|
||||||
|
expect(onViewableItemsChanged).toHaveBeenLastCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
viewableItems: [expect.objectContaining({isViewable: true, key: 'i4'})],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user