@@ -2184,4 +2184,87 @@ describe('ReactIncremental', () => {
21842184 'componentDidUpdate' ,
21852185 ] ) ;
21862186 } ) ;
2187+
2188+ it ( 'should reuse memoized work if pointers are updated before calling lifecycles' , ( ) => {
2189+ let cduNextProps = [ ] ;
2190+ let cduPrevProps = [ ] ;
2191+ let scuNextProps = [ ] ;
2192+ let scuPrevProps = [ ] ;
2193+ let renderCounter = 0 ;
2194+
2195+ function SecondChild ( props ) {
2196+ return < span > { props . children } </ span > ;
2197+ }
2198+
2199+ class FirstChild extends React . Component {
2200+ componentDidUpdate ( prevProps , prevState ) {
2201+ cduNextProps . push ( this . props ) ;
2202+ cduPrevProps . push ( prevProps ) ;
2203+ }
2204+ shouldComponentUpdate ( nextProps , nextState ) {
2205+ scuNextProps . push ( nextProps ) ;
2206+ scuPrevProps . push ( this . props ) ;
2207+ return this . props . children !== nextProps . children ;
2208+ }
2209+ render ( ) {
2210+ renderCounter ++ ;
2211+ return < span > { this . props . children } </ span > ;
2212+ }
2213+ }
2214+
2215+ class Middle extends React . Component {
2216+ render ( ) {
2217+ return (
2218+ < div >
2219+ < FirstChild > { this . props . children } </ FirstChild >
2220+ < SecondChild > { this . props . children } </ SecondChild >
2221+ </ div >
2222+ ) ;
2223+ }
2224+ }
2225+
2226+ function Root ( props ) {
2227+ return (
2228+ < div hidden = { true } >
2229+ < Middle { ...props } />
2230+ </ div >
2231+ ) ;
2232+ }
2233+
2234+ // Initial render of the entire tree.
2235+ // Renders: Root, Middle, FirstChild, SecondChild
2236+ ReactNoop . render ( < Root > A</ Root > ) ;
2237+ ReactNoop . flush ( ) ;
2238+
2239+ expect ( renderCounter ) . toBe ( 1 ) ;
2240+
2241+ // Schedule low priority work to update children.
2242+ // Give it enough time to partially render.
2243+ // Renders: Root, Middle, FirstChild
2244+ ReactNoop . render ( < Root > B</ Root > ) ;
2245+ ReactNoop . flushDeferredPri ( 20 + 30 + 5 ) ;
2246+
2247+ // At this point our FirstChild component has rendered a second time,
2248+ // But since the render is not completed cDU should not be called yet.
2249+ expect ( renderCounter ) . toBe ( 2 ) ;
2250+ expect ( scuPrevProps ) . toEqual ( [ { children : 'A' } ] ) ;
2251+ expect ( scuNextProps ) . toEqual ( [ { children : 'B' } ] ) ;
2252+ expect ( cduPrevProps ) . toEqual ( [ ] ) ;
2253+ expect ( cduNextProps ) . toEqual ( [ ] ) ;
2254+
2255+ // Next interrupt the partial render with higher priority work.
2256+ // The in-progress child content will bailout.
2257+ // Renders: Root, Middle, FirstChild, SecondChild
2258+ ReactNoop . render ( < Root > B</ Root > ) ;
2259+ ReactNoop . flush ( ) ;
2260+
2261+ // At this point the higher priority render has completed.
2262+ // Since FirstChild props didn't change, sCU returned false.
2263+ // The previous memoized copy should be used.
2264+ expect ( renderCounter ) . toBe ( 2 ) ;
2265+ expect ( scuPrevProps ) . toEqual ( [ { children : 'A' } , { children : 'B' } ] ) ;
2266+ expect ( scuNextProps ) . toEqual ( [ { children : 'B' } , { children : 'B' } ] ) ;
2267+ expect ( cduPrevProps ) . toEqual ( [ { children : 'A' } ] ) ;
2268+ expect ( cduNextProps ) . toEqual ( [ { children : 'B' } ] ) ;
2269+ } ) ;
21872270} ) ;
0 commit comments