20

I've just had a look at this discussion about setState() inside componentDidMount().

You can see that after the render() function, the componentDidMount() function will be called by React. When you put a setState() call in componentDidMount() then you are causing the entire component tree be re-rendered not only the current component - not to forget, the current component did just finished with rendering.

And some people suggested to put setState() call inside componentWillMount(). In some cases, I want to get the height of a rendered element and store it as state, and the above method wouldn't work. I also had a look at the React official website, and it suggests to do Ajax call inside componentDidMount(), which again goes against the above idea.

So, am I wrong about putting setState() inside componentDidMount()? If yes, what should I apply as an alternative?

WebDeg Brian
  • 802
  • 1
  • 7
  • 21
  • 1
    Doing an AJAX call in componentDidMount makes sense because you will want to display a placeholder element and then populate it with data – Jared Goguen Sep 04 '18 at 14:04
  • @JaredGoguen Hi, thanks for commenting! I would love to know what's your idea about getting the height of a rendered element without going against the idea – WebDeg Brian Sep 04 '18 at 14:05
  • I can't really comment on your particular use case because you haven't said *why* you need the height of your element, but this is another case where a re-render might make sense once the components know what they're dealing with (assuming there isn't a CSS solution) – Jared Goguen Sep 04 '18 at 14:07
  • 2
    Their comment about the whole component tree will re-render is not accurate. That's not how react virtual dom works – charlietfl Sep 04 '18 at 14:08
  • I don't think calling setState in componentDidMount is necessarily a anti-pattern, but it should only be used when needed and the examples you gave may be two of those cases – Jared Goguen Sep 04 '18 at 14:08
  • @JaredGoguen It would be fine with small applications, but if the component gets huge, the layout may be thrash – WebDeg Brian Sep 04 '18 at 14:12
  • @JaredGoguen I also read about AJAX calls and people said that if `setState()` is put inside a callback then it's completely fine – WebDeg Brian Sep 04 '18 at 14:15
  • @WebDegBrian also depends what you need to do In some cases might be better not to use setState and manipulate the dom yourself if it's a minor layout adjustment in a complex component – charlietfl Sep 04 '18 at 14:16
  • @charlietfl Good idea although it would be really hard to debug – WebDeg Brian Sep 04 '18 at 14:17
  • 1
    can still store whatever changes are made without needing to impact component re-renders. Again...depends on what you are needing to do – charlietfl Sep 04 '18 at 14:22
  • @charlietfl For future reference, please combine all of your ideas and write them as an answer – WebDeg Brian Sep 04 '18 at 14:24
  • I can't really formulate a valuable answer without really knowing more about your situation. Just run with those comments – charlietfl Sep 04 '18 at 14:25
  • @charlietfl, you just need to write a general answer, explain whether putting `setState()` inside `componentDidMount()` is an anti-pattern or not – WebDeg Brian Sep 04 '18 at 14:28

4 Answers4

14

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

Using DidMount makes it clear that data won’t be loaded until after the initial render. This reminds you to set up initial state properly, so you don’t end up with undefined state that causes errors.

Example

TLDR: - If you have all needed data in constructor - assign state there

constructor(props) {
  super(props);
  // Don't call this.setState() here!
  this.state = { counter: 0 };
}
  • Call async action, touch DOM in componentDidMount()
WebDeg Brian
  • 802
  • 1
  • 7
  • 21
Buggy
  • 3,539
  • 1
  • 21
  • 38
4

Your case with unknowing the height of a rendered element might be a valid excuse to use setState inside componentDidMount. However in such a case I would definitely add another lifecycle method,shouldComponentUpdate, to control the rerender issue.

milkersarac
  • 3,399
  • 3
  • 31
  • 31
  • Thanks for answering! One little question, if the height state affects how the components are rendered, then `shouldComponentUpdate` is not needed right? – WebDeg Brian Sep 04 '18 at 14:33
  • @WebDegBrian yes, if you want to render depending on the state computed, then you may not need `shouldComponentUpdate`, but this seems like a poor use of react to me, generally you do not want to overcontrol each component pixel by pixel in your js or ts code. – milkersarac Sep 05 '18 at 06:06
  • 1
    you should not use `shouldComponentUpdate` to control renderer issues, [it only exists as a performance optimization](https://reactjs.org/docs/react-component.html#shouldcomponentupdate). The doc explicitly says: "Do not rely on it to “prevent” a rendering, as this can lead to bugs". – Norbert Biró Jul 28 '19 at 22:45
3

Normally you don't want to do synchronous state setting inside componentDidMount, you should just put it in the constructor. However element height is a bit unique, since you can't actually get the height of an element until it's mounted and rendered into the DOM.

So normally not, but in the case of element height it's ok to do it in componentDidMount.

Jayce444
  • 8,725
  • 3
  • 27
  • 43
0

You can do Async call and you must do it inside the componentDidMount() lifecycle hook. But this will call the render method again.

If you don't want the re-render to happen again, use the shouldComponentUpdate() method to prevent re-rendering the DOM. Example as follows:

shouldComponentUpdate (nextProps, nextState) {
    // check the condition here
    if (nextState.something !== 'value') {
         // stop re-rendering
         return false;
    }
    // continue rendering
    return true;
}
the_haystacker
  • 1,585
  • 18
  • 20