1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @emails react-core
    
  8.  */
    
  9. 
    
  10. 'use strict';
    
  11. 
    
  12. let React;
    
  13. let ReactDOM;
    
  14. let ReactTestUtils;
    
  15. 
    
  16. let TestComponent;
    
  17. 
    
  18. describe('refs-destruction', () => {
    
  19.   beforeEach(() => {
    
  20.     jest.resetModules();
    
  21. 
    
  22.     React = require('react');
    
  23.     ReactDOM = require('react-dom');
    
  24.     ReactTestUtils = require('react-dom/test-utils');
    
  25. 
    
  26.     class ClassComponent extends React.Component {
    
  27.       render() {
    
  28.         return null;
    
  29.       }
    
  30.     }
    
  31. 
    
  32.     TestComponent = class extends React.Component {
    
  33.       theInnerDivRef = React.createRef();
    
  34.       theInnerClassComponentRef = React.createRef();
    
  35. 
    
  36.       render() {
    
  37.         if (this.props.destroy) {
    
  38.           return <div />;
    
  39.         } else if (this.props.removeRef) {
    
  40.           return (
    
  41.             <div>
    
  42.               <div />
    
  43.               <ClassComponent />
    
  44.             </div>
    
  45.           );
    
  46.         } else {
    
  47.           return (
    
  48.             <div>
    
  49.               <div ref={this.theInnerDivRef} />
    
  50.               <ClassComponent ref={this.theInnerClassComponentRef} />
    
  51.             </div>
    
  52.           );
    
  53.         }
    
  54.       }
    
  55.     };
    
  56.   });
    
  57. 
    
  58.   it('should remove refs when destroying the parent', () => {
    
  59.     const container = document.createElement('div');
    
  60.     const testInstance = ReactDOM.render(<TestComponent />, container);
    
  61. 
    
  62.     expect(
    
  63.       ReactTestUtils.isDOMComponent(testInstance.theInnerDivRef.current),
    
  64.     ).toBe(true);
    
  65.     expect(testInstance.theInnerClassComponentRef.current).toBeTruthy();
    
  66. 
    
  67.     ReactDOM.unmountComponentAtNode(container);
    
  68. 
    
  69.     expect(testInstance.theInnerDivRef.current).toBe(null);
    
  70.     expect(testInstance.theInnerClassComponentRef.current).toBe(null);
    
  71.   });
    
  72. 
    
  73.   it('should remove refs when destroying the child', () => {
    
  74.     const container = document.createElement('div');
    
  75.     const testInstance = ReactDOM.render(<TestComponent />, container);
    
  76.     expect(
    
  77.       ReactTestUtils.isDOMComponent(testInstance.theInnerDivRef.current),
    
  78.     ).toBe(true);
    
  79.     expect(testInstance.theInnerClassComponentRef.current).toBeTruthy();
    
  80. 
    
  81.     ReactDOM.render(<TestComponent destroy={true} />, container);
    
  82. 
    
  83.     expect(testInstance.theInnerDivRef.current).toBe(null);
    
  84.     expect(testInstance.theInnerClassComponentRef.current).toBe(null);
    
  85.   });
    
  86. 
    
  87.   it('should remove refs when removing the child ref attribute', () => {
    
  88.     const container = document.createElement('div');
    
  89.     const testInstance = ReactDOM.render(<TestComponent />, container);
    
  90. 
    
  91.     expect(
    
  92.       ReactTestUtils.isDOMComponent(testInstance.theInnerDivRef.current),
    
  93.     ).toBe(true);
    
  94.     expect(testInstance.theInnerClassComponentRef.current).toBeTruthy();
    
  95. 
    
  96.     ReactDOM.render(<TestComponent removeRef={true} />, container);
    
  97. 
    
  98.     expect(testInstance.theInnerDivRef.current).toBe(null);
    
  99.     expect(testInstance.theInnerClassComponentRef.current).toBe(null);
    
  100.   });
    
  101. 
    
  102.   it('should not error when destroying child with ref asynchronously', () => {
    
  103.     class Modal extends React.Component {
    
  104.       componentDidMount() {
    
  105.         this.div = document.createElement('div');
    
  106.         document.body.appendChild(this.div);
    
  107.         this.componentDidUpdate();
    
  108.       }
    
  109. 
    
  110.       componentDidUpdate() {
    
  111.         ReactDOM.render(<div>{this.props.children}</div>, this.div);
    
  112.       }
    
  113. 
    
  114.       componentWillUnmount() {
    
  115.         const self = this;
    
  116.         // some async animation
    
  117.         setTimeout(function () {
    
  118.           expect(function () {
    
  119.             ReactDOM.unmountComponentAtNode(self.div);
    
  120.           }).not.toThrow();
    
  121.           document.body.removeChild(self.div);
    
  122.         }, 0);
    
  123.       }
    
  124. 
    
  125.       render() {
    
  126.         return null;
    
  127.       }
    
  128.     }
    
  129. 
    
  130.     class AppModal extends React.Component {
    
  131.       render() {
    
  132.         return (
    
  133.           <Modal>
    
  134.             <a ref={React.createRef()} />
    
  135.           </Modal>
    
  136.         );
    
  137.       }
    
  138.     }
    
  139. 
    
  140.     class App extends React.Component {
    
  141.       render() {
    
  142.         return this.props.hidden ? null : <AppModal onClose={this.close} />;
    
  143.       }
    
  144.     }
    
  145. 
    
  146.     const container = document.createElement('div');
    
  147.     ReactDOM.render(<App />, container);
    
  148.     ReactDOM.render(<App hidden={true} />, container);
    
  149.     jest.runAllTimers();
    
  150.   });
    
  151. });