0

I have the following code, which is a react component rendering a huge component. As long as the huge component has not been finished with rendering, a loading indicator is shown.

import * as React from "react";
import ReactDOM from "react-dom";

import {HUGEComponent} from "./huge.tsx";

class App extends React.Component<{}, {loading: boolean}> {
  constructor(props: {}) {
    super(props);
    this.state = {loading: true};
  }

  componentDidMount() {
    setTimeout(() => this.setState({loading: false}), 0);
  }

  render() {
    return this.state.loading ? <p>Loading...</p> : <HUGEComponent />;
  }
}

ReactDOM.render(<App />, document.getElementById("root"));

The setTimeout function inside componentDidMount ensures, that the state update is happening only after the HUGEComponent has been loaded (I used 10000 paragraphs of lorem ipsum as HUGEComponent). Without setTimeout, the state is updated immediately and the loading indicator is not shown.

So my question is, why does that work with setTimeout? I know, that it pushes the state update to the message queue, which will therefore be executed after all other stuff has been done. But because a ternary operator (lazy evaluation) is used, actual rendering of HUGEComponent should wait until the state has been updated, so that the state update occurs before rendering it, but that seems to be not true. The state is actually not updated as long as <HUGEComponent /> has not been evaluated. So why is <HUGEComponent /> evaluated before the state update despite the lazy evaluation in the ternary operator?

Peter Lehnhardt
  • 4,375
  • 1
  • 14
  • 33

2 Answers2

1

I think your interpretation of events is wrong. Actually state update is not waiting for HugeComponent to evaluate, but it is happening immediately which triggers a rerender and causes HugeComponent to evaluate. While HugeComponent is evaluating you won't see any change in your DOM so this is what makes loading text visible while HugeComponent is evaluating.

For the case when setTimeout is not used @tan-dat 's answer makes sense. When setTimeout is not used user will not see intermediate state(which is your loading text).

laltin
  • 1,134
  • 12
  • 20
0

Maybe it because of this.

You may call setState() immediately in componentDidMount(). It will trigger an extra rendering, but it will happen before the browser updates the screen. This guarantees that even though the render() will be called twice in this case, the user won’t see the intermediate state. Use this pattern with caution because it often causes performance issues. In most cases, you should be able to assign the initial state in the constructor() instead. It can, however, be necessary for cases like modals and tooltips when you need to measure a DOM node before rendering something that depends on its size or position. React docs

A similar question that can explain why: Is setState() inside componentDidMount() considered an anti-pattern

Tan Dat
  • 2,888
  • 1
  • 17
  • 39