Skip to content
Advertisement

Wait for browser to render element in React

I’m using CSS transition with top and left to animate the movement of an element in React.

To achieve this, the component must first be rendered with top: 0; left: 0, and then re-rendered with altered top and left properties to start the transition.

This is my component:

export default class Animator extends React.Component{

    render(){
        var top = 0;
        var left = 0;

        if(this.state.moving){
            top = this.props.top;
            left = this.props.left;
        }

        return(
            <div 
            className="red-box animator" 
            style={{
                top: `{top}px`,
                left: `{left}px`}}>
            </div>
        );
    }

    componentDidMount(){
        this.setState({moving: true});
    }
}
.animator{
    position: absolute;
    transition: top 0.5s, left 0.5s;
}

.red-box{
    width: 50px;
    height: 50px;
    background-color: red;
}

I expected the component to be rendered by the browser before componentDidMount fires, but I found that this isn’t the case.

As a result, the top and left properties are updated too soon and the component appears immediately in its destination position with no transition.

Changing my componentDidMount function to have a delay solves the problem, but too short of a delay makes the functionality unreliable (especially on slower devices), and I don’t want a longer delay as I want the element to be moving as soon as it appears on the screen.

componentDidMount(){
    setTimeout(() => {
        this.setState({moving: true});
    }, 150);
}

Is there a way to ensure that I only update the state once the browser has rendered the element?

Advertisement

Answer

Browsers optimize render and are not rerendering stuff that changed in the same animation frame. They merge changes and render the end result. To mitigate that you may use requestAnimationFrame trick:

componenetDidMount() {
  self.requestAnimationFrame(() => this.setState({ moving: true })
}

Keep in mind that though requestAnimationFrame is widely supported among browsers some really old ones might need a polifill or prefix to make it work.

User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement