Ephasme
Ephasme

Reputation: 288

Why doesn't React update dom properly?

I have this ReactJS class :

import React from 'react';

export default class Test extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      status: 'main',
    };
  }

  applyTransition() {
    this.setState({
      status: 'leaving',
    }, () => {
      setTimeout(() => {
        this.setState({
          status: 'entering'
        }, () => setTimeout(() => {
          this.setState({
            status: 'main',
          });
        }, 1000));
      }, 1)
    });
  }
  handleClick() {
    this.applyTransition();
  }
  computeStyle() {
    switch (this.state.status) {
      case 'leaving':
        return {
          transform: 'translate(-100%)',
        };
      case 'entering':
        return {
          transform: 'translate(0%)',
          transition: 'transform 1s',
        };
      case 'main':
        return {
          transform: 'translate(0%)',
        };
    }
  }

  render() {
    const style = this.computeStyle();
    return (
      <div>
        <div style={_.merge({}, style, {
            display: 'flex',
            flexOrientation: 'row',
          })}>
          <div>1</div>
          <div>2</div>
          <div>3</div>
        </div>
        <button onClick={() => this.handleClick()}>Click</button>
      </div>
    );
  }
}

My workflow is the following, I have a "main" state in which the content is displayed normally, I have a "beforeTransition" state in which I want the content to be translated to the left and a "transition" in which I want the content to come back in place with a CSS transition.

It seems that react does not update the DOM properly if I do not use a timeout of 1 ms. Consequently, the transition does not work. Why?

EDIT:

So how do I ensure that my CSS transition workflow is completed? I WANT that DOM to be updated and rendered properly at each changes of my state.status variable.

I don't want to use ReactCSSTransitionGroup because I want to understand how I can create some custom CSS transitions from scratch.

Upvotes: 0

Views: 181

Answers (2)

synthomat
synthomat

Reputation: 774

Even though I'm not super familiar with the way of how the rendering queue in browsers works, I assume that you cannot assign contrary css properties with no delay between them. Like transition to left - then to right again.

And it probably has nothing to with React per se.

Look here: https://jsbin.com/geqogeqifa/edit?html,output

EDIT: (heavily guessing)
As rendering the output and JS are using the same event queue in the browsers, no re-rendering happens between your "leaving" and "entering" css property assignment –  which causes the last property override the first one even before the first one was rendered.

So my guess is, by using a timeout, you insert another event in the event queue, which is called after a previous render event happened…

Pseudo Event Queue:

Without timeout:

Event(JS): css(leaving), css(entering)
Event(Render) // renders only entering

With timeout:

Event(JS): css(leaving), setTimeOut(css(entering))
Event(Render) // renders leaving
Event(timeOut): css(entering)
Event(Render) // renders entering

Upvotes: 1

Mark McKelvy
Mark McKelvy

Reputation: 3536

If you are just trying to animate translating an existing element (which appears to be the case based on your code) then this would be a much simpler approach:

constructor() {
  super();
  this.state = {enter: false};
}

handleClick() {
  this.setState({
    enter: true
  });
}

computeStyle(enter) {
  return {
    transform: enter ? 'translate(0%)' : 'translate(-100%)',
    transition: 'transform 1s'
  };
}

render() {
  const style = this.computerStyle(this.state.enter);

  // divs and stuff with your style

  <button onClick={this.handleClick} />
}

Upvotes: 0

Related Questions