0

I just would like to show the word "Jack" first, and then after a few seconds, to show another number: 20. But React.js always show them together at the same time. Btw, I know how to use timeout, I just confused why the following code did not run line by line?!

import React from 'react';
import '../index.scss'

class MyForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      username: '',
      age: null,
    };
  }

  longTimeFunction(input) {
    var i = 0;
    while(i < input) {
      console.log(i)
      i = i + 1
    }
  };

  componentDidMount() {
    this.setState({ username: 'JACK' })
    this.longTimeFunction(30000);
    this.setState({ age: '20' })
  }

  render() {
    return (
      <h1>Hello: {this.state.username}  {this.state.age}</h1>
    );
  }
}

export default MyForm;
Jack
  • 5,540
  • 13
  • 65
  • 113
  • Right, so, `this.longTimeFunction(30000);` prevents the first line from taking effect because it prevents the event loop from continuing. Once it's done, the second one runs, and the two are applied simultaneously as if they were ran one after the other without the middle line. That function should never be used in any javascript project, regardless of needs. it is literally useless. – Kevin B Nov 04 '19 at 22:04

2 Answers2

2

Use setTimeout:

class MyForm extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            username: '',
            age: null,
        };
    }

    componentDidMount() {
        this.setState({ 'username': 'JACK' })
        const vm = this;
        setTimeout(function () {

            vm.setState({ age: '20' })
        }, 30000)
    }

    render() {
        return (
            <h1>Hello: {this.state.username}  {this.state.age}</h1>
        );
    }
}

export default MyForm;

ETA: Typically, you don't want to block execution in JS, you want to use a callback of some sort to defer execution of a certain process.

gabriel.hayes
  • 2,267
  • 12
  • 15
  • Actually it didn't answer my question, it might my fault. I know how to use timeout, I just confused why the above code did not run line by line?! – Jack Nov 04 '19 at 22:03
  • There's no appropriate way to block JavaScript synchronously because you don't want to ever block it synchronously. Consider that if you did block it synchronously, every instance of this component would delay execution for something that presumably only matters to the given Component instance. I would reconsider your approach to the problem. – gabriel.hayes Nov 04 '19 at 22:06
  • 1
    Though, if you -really- want to block synchronously, while not recommended, you can [use a while loop](https://stackoverflow.com/questions/10527503/how-do-i-write-a-blocking-synchronous-method-in-javascript) (though, behavior isn't consistent across browsers; some browsers will snuff out script execution, some will just become unresponsive, Chrome seems to handle it best) – gabriel.hayes Nov 04 '19 at 22:09
1

First reason is that you block the main thread that is also used to paint the screen so you won't be able to see any changes until the whole code finishes like in this basic example:

function longTimeFunction(time) {
  const end = Date.now() + time;
  while (Date.now() < end);
}

document.body.appendChild(document.createTextNode("Hello "));
// at this point "Hello " is already in the DOM 
// but you can't see it because JS is still running 
// and blocks the browser from updating the screen
longTimeFunction(3000);
document.body.appendChild(document.createTextNode("World"));

The second reason is that React uses an optimization mechanism for batching state updates. In React v16 this optimization is applied for lifecycle methods and event handlers (but it's turned off for things like timers and promises where React can't know when the code is completed). So, conceptually, both setState calls are dealt with at the same time and cause only one DOM update (render).

marzelin
  • 10,790
  • 2
  • 30
  • 49