@@ -33,6 +33,16 @@ const PASSIVE = (() => {
3333 return hasSupport ;
3434} ) ( ) ? { passive : true } : false ;
3535
36+ const UNSTABLE_MESSAGE = 'ReactList failed to reach a stable state.' ;
37+
38+ const isEqualSubset = ( a , b ) => {
39+ for ( let key in b ) if ( a [ key ] !== b [ key ] ) return false ;
40+
41+ return true ;
42+ } ;
43+
44+ const isEqual = ( a , b ) => isEqualSubset ( a , b ) && isEqualSubset ( b , a ) ;
45+
3646module . exports = class ReactList extends Component {
3747 static displayName = 'ReactList' ;
3848
@@ -72,6 +82,8 @@ module.exports = class ReactList extends Component {
7282 this . constrain ( initialIndex , pageSize , itemsPerRow , this . props ) ;
7383 this . state = { from, size, itemsPerRow} ;
7484 this . cache = { } ;
85+ this . prevPrevState = { } ;
86+ this . unstable = false ;
7587 }
7688
7789 componentWillReceiveProps ( next ) {
@@ -85,15 +97,37 @@ module.exports = class ReactList extends Component {
8597 this . updateFrame ( this . scrollTo . bind ( this , this . props . initialIndex ) ) ;
8698 }
8799
88- componentDidUpdate ( ) {
89- this . updateFrame ( ) ;
100+ componentDidUpdate ( prevProps , prevState ) {
101+
102+ // If the list has reached an unstable state, prevent an infinite loop.
103+ if ( this . unstable ) return ;
104+
105+ // Update calculations if props have changed between renders.
106+ const propsEqual = isEqual ( this . props , prevProps ) ;
107+ if ( ! propsEqual ) {
108+ this . prevPrevState = { } ;
109+ return this . updateFrame ( ) ;
110+ }
111+
112+ // Check for ping-ponging between the same two states.
113+ const stateEqual = isEqual ( this . state , prevState ) ;
114+ const pingPong = ! stateEqual && isEqual ( this . state , this . prevPrevState ) ;
115+
116+ // Ping-ponging between states means this list is unstable, log an error.
117+ if ( pingPong ) {
118+ this . unstable = true ;
119+ return console . error ( UNSTABLE_MESSAGE ) ;
120+ }
121+
122+ // Update calculations if state has changed between renders.
123+ this . prevPrevState = prevState ;
124+ if ( ! stateEqual ) this . updateFrame ( ) ;
90125 }
91126
92127 maybeSetState ( b , cb ) {
93- const a = this . state ;
94- for ( let key in b ) if ( a [ key ] !== b [ key ] ) return this . setState ( b , cb ) ;
128+ if ( isEqualSubset ( this . state , b ) ) return cb ( ) ;
95129
96- cb ( ) ;
130+ this . setState ( b , cb ) ;
97131 }
98132
99133 componentWillUnmount ( ) {
0 commit comments