reactjswindow-resizereact-lifecycle

componentDidUpdate is not fired


In my source code, the height of the component EditorBasic that I measure by .current.clientHeight changes when resizing the window, which is not correct. To reproduce the bug, I wrote the following smaller code.

I used aRef to refer to EditorBasic and expected to get its height. I expect the following code to have the following behaviour:

  1. When we click on "Click to toggle EditorBasic", by updateTrigger, EditorBasic will trigger handleResize.

  2. handleResize calls calculateNextHeight which can print the height of the EditorBasic so that I could check.

But I cannot pass the 1st step; changing updateTrigger cannot trigger handleResize; the log componentDidUpdate is not printed.

Does anyone know why?

StackBlitz: https://stackblitz.com/edit/react-ts-j4pm8x?file=App.tsx,EditorBasic.tsx

import * as React from 'react';
import EditorBasic from './EditorBasic';

interface IAppProps {}

interface IAppState {
  showEditorBasic: boolean;
  updateTrigger: number;
}

export default class App extends React.Component<IAppProps, IAppState> {
  aRef: any;

  constructor(props) {
    super(props);
    this.state = {
      showEditorBasic: false,
      updateTrigger: 0,
    };
    this.aRef = React.createRef();
  }

  calculateNextHeight = () => {
    console.log(
      'this.aRef.current.clientHeight',
      this.aRef.current.clientHeight
    );
  };

  render() {
    return (
      <div>
        <div
          onClick={() =>
            this.setState({
              showEditorBasic: !this.state.showEditorBasic,
              updateTrigger: this.state.updateTrigger + 1,
            })
          }
        >
          Click to toggle EditorBasic
        </div>
        {this.state.showEditorBasic && (
          <div ref={this.aRef}>
            <EditorBasic
              calculateNextHeight={this.calculateNextHeight}
              updateTrigger={this.state.updateTrigger}
            />
          </div>
        )}
      </div>
    );
  }
}
import * as React from 'react';

interface IEditorBasicProps {
  calculateNextHeight: any;
  updateTrigger;
}

interface IEditorBasicState {}

export default class EditorBasic extends React.Component<
  IEditorBasicProps,
  IEditorBasicState
> {
  handleResize() {
    // console.log('handleResize');
    this.props.calculateNextHeight();
  }

  componentDidMount() {
    // console.log('componentDidMount');
    window.addEventListener('resize', this.handleResize.bind(this));
  }

  componentDidUpdate(prevProps) {
    console.log('componentDidUpdate');
    if (this.props['updateTrigger'] !== prevProps['updateTrigger']) {
      this.handleResize();
    }
  }

  render() {
    return (
      <div>
        <br />
        <br />
        EditorBasic {this.props.updateTrigger}
        <br />
        <br />
      </div>
    );
  }
}

Solution

  • The property showEditorBasic is completely recreateing the BasicEditor. In that case, the component will only trigger the componentDidMount function.

    Try to call handleResize inside componentDidMount and render the aRef div outside your if statement.

    App

    import * as React from 'react';
    import EditorBasic from './EditorBasic';
    
    interface IAppProps {}
    
    interface IAppState {
      showEditorBasic: boolean;
      updateTrigger: number;
    }
    
    export default class App extends React.Component<IAppProps, IAppState> {
      aRef: any;
    
      constructor(props) {
        super(props);
        this.state = {
          showEditorBasic: false,
          updateTrigger: 0,
        };
        this.aRef = React.createRef();
      }
    
      calculateNextHeight = () => {
        console.log(
          'this.aRef.current.clientHeight',
          this.aRef.current.clientHeight
        );
      };
    
      render() {
        return (
          <div>
            <div
              onClick={() =>
                this.setState({
                  showEditorBasic: !this.state.showEditorBasic,
                  updateTrigger: this.state.updateTrigger + 1,
                })
              }
            >
              Click to toggle EditorBasic
            </div>
              <div ref={this.aRef}>
                {this.state.showEditorBasic && (
                  <EditorBasic
                    calculateNextHeight={this.calculateNextHeight}
                    updateTrigger={this.state.updateTrigger}
                  />
                )}
            </div>
          </div>
        );
      }
    }
    

    EditorBasic

    import * as React from 'react';
    
    interface IEditorBasicProps {
      calculateNextHeight: any;
      updateTrigger;
    }
    
    interface IEditorBasicState {}
    
    export default class EditorBasic extends React.Component<
      IEditorBasicProps,
      IEditorBasicState
    > {
      handleResize() {
        // console.log('handleResize');
        this.props.calculateNextHeight();
      }
    
      componentDidMount() {
        // console.log('componentDidMount');
        window.addEventListener('resize', this.handleResize.bind(this));
        this.handleResize();
      }
    
      componentDidUpdate(prevProps) {
        console.log('componentDidUpdate');
        if (this.props['updateTrigger'] !== prevProps['updateTrigger']) {
          this.handleResize();
        }
      }
    
      render() {
        return (
          <div>
            <br />
            <br />
            EditorBasic {this.props.updateTrigger}
            <br />
            <br />
          </div>
        );
      }
    }