Antonio Gamiz Delgado
Antonio Gamiz Delgado

Reputation: 2103

How to update state just after rendering

I have the following component:

import React from 'react';
import './styles.css';
import ToolTip from '../../Common/components/ToolTip/ToolTip';

export default class RouteTitleTooltipComponent extends React.Component {
    constructor(props) {
        super(props);
        this.titleParagraphRef = React.createRef();
        this._tooltipTimer = null;
        this.state = { shouldPopupBeEnabled: false, isTooltipShown: false };

        this._showTooltip = this._showTooltip.bind(this);
        this._hideTooltip = this._hideTooltip.bind(this);
    }

    componentDidMount() {
        const { scrollWidth, clientWidth } = this.titleParagraphRef.current;
        const shouldPopupBeEnabled = scrollWidth > clientWidth;
        this.setState({ shouldPopupBeEnabled });
    }

    _showTooltip() {
        this._tooltipTimer = setTimeout(
            () => {
                this.setState({ isTooltipShown: true });
            }, 1000,
        );
    }

    _hideTooltip() {
        clearTimeout(this._tooltipTimer);
        this.setState({ isTooltipShown: false });
    }

    render() {
        const { shouldPopupBeEnabled, isTooltipShown } = this.state;
        const { message } = this.props;

        return (
            <ToolTip
                message="Tooltip!!"
                popoverOpen={shouldPopupBeEnabled && isTooltipShown}
            >
                <div
                    ref={this.titleParagraphRef}
                    onMouseOver={this._showTooltip}
                >
                    {message}
                </div>
            </ToolTip>
        );
    }
}

This basically renders a floating tooltip over a div element if the message inside of it is bigger than the container. To do that, I use scrollWidth and clientWidth of the div element using a React reference. To detect those values I use componentDidMount, but this only works in full renders of the component. That is, if I have the component visible and reload the page, both values are equal to 0 and it does not work.

In addition, if I change the message, it does not work either because the component is already mounted.

So what I want is to change the state right after the component is mounted or updated so that the react reference is rendered and clientWidth and scrollWidth are not 0.

I have tried replace componentDidUpdate instead of componentDidMount but it's not a good practica to use setState inside componentDidUpdate.

Any solution?

Upvotes: 0

Views: 231

Answers (1)

Fahad Shinwari
Fahad Shinwari

Reputation: 968

First you should know that componentDidMount will execute only once. Therefor you can go for componentDidUpdate but don't forget to put a condition as it will render in a loop.

componentDidUpdate(prevProps,prevState) {

      const shouldPopupBeEnabled = scrollWidth > clientWidth;

      if (shouldPopupBeEnabled  !== this.state.shouldPopupBeEnabled ) {

          this.setState({shouldPopupBeEnabled  });

   }
 }

Or you can go for functional components and use useEffect which will only render again if state changes.

useEffect(() => {
  console.log('mounted');
}, [shouldPopupBeEnabled]) // It will re render id `shouldPopupBeEnabled` changes 

Upvotes: 1

Related Questions