@@ -755,7 +755,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
755
755
static _createRenderMask (
756
756
props : Props ,
757
757
cellsAroundViewport : { first : number , last : number } ,
758
- lastFocusedItem : ?number ,
758
+ additionalRegions ? : ?$ReadOnlyArray < { first : number , last : number } > ,
759
759
) : CellRenderMask {
760
760
const itemCount = props . getItemCount ( props . data ) ;
761
761
@@ -768,17 +768,12 @@ class VirtualizedList extends React.PureComponent<Props, State> {
768
768
769
769
const renderMask = new CellRenderMask ( itemCount ) ;
770
770
771
- // Keep the items around the last focused rendered, to allow for keyboard
772
- // navigation
773
- if ( lastFocusedItem ) {
774
- const first = Math . max ( 0 , lastFocusedItem - 1 ) ;
775
- const last = Math . min ( itemCount - 1 , lastFocusedItem + 1 ) ;
776
- renderMask . addCells ( { first, last} ) ;
777
- }
778
-
779
771
if ( itemCount > 0 ) {
780
- if ( cellsAroundViewport . last >= cellsAroundViewport . first ) {
781
- renderMask . addCells ( cellsAroundViewport ) ;
772
+ const allRegions = [ cellsAroundViewport , ...( additionalRegions ?? [ ] ) ] ;
773
+ for ( const region of allRegions ) {
774
+ if ( region . last >= region . first ) {
775
+ renderMask . addCells ( region ) ;
776
+ }
782
777
}
783
778
784
779
// The initially rendered cells are retained as part of the
@@ -956,23 +951,25 @@ class VirtualizedList extends React.PureComponent<Props, State> {
956
951
this . _fillRateHelper . deactivateAndFlush ( ) ;
957
952
}
958
953
959
- static getDerivedStateFromProps ( newProps : Props , prevState : State ) : State {
954
+ UNSAFE_componentWillReceiveProps ( newProps : Props ) {
960
955
// first and last could be stale (e.g. if a new, shorter items props is passed in), so we make
961
956
// sure we're rendering a reasonable range here.
962
957
const itemCount = newProps . getItemCount ( newProps . data ) ;
963
- if ( itemCount === prevState . renderMask . numCells ( ) ) {
964
- return prevState ;
965
- }
966
-
967
- const constrainedCells = VirtualizedList . _constrainToItemCount (
968
- prevState . cellsAroundViewport ,
969
- newProps ,
970
- ) ;
958
+ if ( itemCount !== this . state . renderMask . numCells ( ) ) {
959
+ const constrainedCells = VirtualizedList . _constrainToItemCount (
960
+ this . state . cellsAroundViewport ,
961
+ newProps ,
962
+ ) ;
971
963
972
- return {
973
- cellsAroundViewport : constrainedCells ,
974
- renderMask : VirtualizedList . _createRenderMask ( newProps , constrainedCells ) ,
975
- } ;
964
+ this . setState ( {
965
+ cellsAroundViewport : constrainedCells ,
966
+ renderMask : VirtualizedList . _createRenderMask (
967
+ newProps ,
968
+ constrainedCells ,
969
+ this . _getNonViewportRenderRegions ( ) ,
970
+ ) ,
971
+ } ) ;
972
+ }
976
973
}
977
974
978
975
_pushCells (
@@ -1016,7 +1013,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
1016
1013
prevCellKey = { prevCellKey }
1017
1014
onUpdateSeparators = { this . _onUpdateSeparators }
1018
1015
onLayout = { e => this . _onCellLayout ( e , key , ii ) }
1019
- onFocusCapture = { e => this . _onCellFocusCapture ( ii ) }
1016
+ onFocusCapture = { e => this . _onCellFocusCapture ( key ) }
1020
1017
onUnmount = { this . _onCellUnmount }
1021
1018
parentProps = { this . props }
1022
1019
ref = { ref => {
@@ -1364,7 +1361,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
1364
1361
_averageCellLength = 0 ;
1365
1362
// Maps a cell key to the set of keys for all outermost child lists within that cell
1366
1363
_cellKeysToChildListKeys : Map < string , Set < string > > = new Map ( ) ;
1367
- _cellRefs = { } ;
1364
+ _cellRefs : { [ string ] : ? CellRenderer } = { } ;
1368
1365
_fillRateHelper: FillRateHelper;
1369
1366
_frames = { } ;
1370
1367
_footerLength = 0;
@@ -1376,7 +1373,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
1376
1373
_hiPriInProgress: boolean = false; // flag to prevent infinite hiPri cell limit update
1377
1374
_highestMeasuredFrameIndex = 0;
1378
1375
_indicesToKeys: Map< number , string > = new Map();
1379
- _lastFocusedItem : ?number = null;
1376
+ _lastFocusedCellKey : ?string = null;
1380
1377
_nestedChildLists: Map<
1381
1378
string ,
1382
1379
{
@@ -1485,12 +1482,12 @@ class VirtualizedList extends React.PureComponent<Props, State> {
1485
1482
this._updateViewableItems(this.props.data);
1486
1483
}
1487
1484
1488
- _onCellFocusCapture ( itemIndex : number ) {
1489
- this . _lastFocusedItem = itemIndex ;
1485
+ _onCellFocusCapture ( cellKey : string ) {
1486
+ this . _lastFocusedCellKey = cellKey ;
1490
1487
const renderMask = VirtualizedList . _createRenderMask (
1491
1488
this . props ,
1492
1489
this . state . cellsAroundViewport ,
1493
- this . _lastFocusedItem ,
1490
+ this . _getNonViewportRenderRegions ( ) ,
1494
1491
) ;
1495
1492
1496
1493
if ( ! renderMask . equals ( this . state . renderMask ) ) {
@@ -1926,7 +1923,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
1926
1923
const renderMask = VirtualizedList . _createRenderMask (
1927
1924
props ,
1928
1925
cellsAroundViewport ,
1929
- this . _lastFocusedItem ,
1926
+ this . _getNonViewportRenderRegions ( ) ,
1930
1927
) ;
1931
1928
1932
1929
if (
@@ -1998,6 +1995,57 @@ class VirtualizedList extends React.PureComponent<Props, State> {
1998
1995
return frame ;
1999
1996
} ;
2000
1997
1998
+ _getNonViewportRenderRegions = ( ) : $ReadOnlyArray < {
1999
+ first : number ,
2000
+ last : number ,
2001
+ } > => {
2002
+ // Keep a viewport's worth of content around the last focused cell to allow
2003
+ // random navigation around it without any blanking. E.g. tabbing from one
2004
+ // focused item out of viewport to another.
2005
+ if (
2006
+ ! ( this . _lastFocusedCellKey && this . _cellRefs [ this . _lastFocusedCellKey ] )
2007
+ ) {
2008
+ return [ ] ;
2009
+ }
2010
+
2011
+ const lastFocusedCellRenderer = this . _cellRefs [ this . _lastFocusedCellKey ] ;
2012
+ const focusedCellIndex = lastFocusedCellRenderer . props . index ;
2013
+ const itemCount = this . props . getItemCount ( this . props . data ) ;
2014
+
2015
+ // The cell may have been unmounted and have a stale index
2016
+ if (
2017
+ focusedCellIndex >= itemCount ||
2018
+ this . _indicesToKeys . get ( focusedCellIndex ) !== this . _lastFocusedCellKey
2019
+ ) {
2020
+ return [ ] ;
2021
+ }
2022
+
2023
+ let first = focusedCellIndex ;
2024
+ let heightOfCellsBeforeFocused = 0 ;
2025
+ for (
2026
+ let i = first - 1 ;
2027
+ i >= 0 && heightOfCellsBeforeFocused < this . _scrollMetrics . visibleLength ;
2028
+ i --
2029
+ ) {
2030
+ first -- ;
2031
+ heightOfCellsBeforeFocused += this . _getFrameMetricsApprox ( i ) . length ;
2032
+ }
2033
+
2034
+ let last = focusedCellIndex ;
2035
+ let heightOfCellsAfterFocused = 0 ;
2036
+ for (
2037
+ let i = last + 1 ;
2038
+ i < itemCount &&
2039
+ heightOfCellsAfterFocused < this . _scrollMetrics . visibleLength ;
2040
+ i ++
2041
+ ) {
2042
+ last ++ ;
2043
+ heightOfCellsAfterFocused += this . _getFrameMetricsApprox ( i ) . length ;
2044
+ }
2045
+
2046
+ return [ { first , last } ] ;
2047
+ } ;
2048
+
2001
2049
_updateViewableItems ( data : any ) {
2002
2050
const { getItemCount} = this . props ;
2003
2051
0 commit comments