0

To realize the effect of componentDidMount(), I wrote the following code segment:

class Blink extends Component {
  constructor(props) {
    super(props);

    this.state = {output: 'c'};
  }
  componentWillMount() {
    this.state.output += 'w';
  }
  render() {
    this.state.output += 'r';
    let display = this.state.output;
     return (
       <Text>{display}</Text>
     );
  }
  componentDidMount() {
    this.state.output += 'd';
    this.setState();
  }
}

export default class BlinkApp extends Component {
  render() {
    return (
      <View>
        <Blink text='I love to blink' />
        <Blink text='Yes blinking is so great' />
        <Blink text='Why did they ever take this out of HTML' />
        <Blink text='Look at me look at me look at me' />
      </View>
    );
  }

I expected to see four lines of 'cwrdr' according to two reasons:

  1. The execution order: constructor(props)->componentWillMount()->render()->componentDidMount()

  2. setState() called in componentDidMount() will invoke render() again.

Please kindly share your thoughts on my code and reasoning. Much appreciated.

phr.phr
  • 11
  • 1

2 Answers2

3

You've made the mistake of mutating state, by using this line this.state.output += 'd';. By doing so, React no longer knows when it is time to trigger a re-render of your application.

Instead, you want to use setState({output : this.state.output + 'd'}). This assigns a new object to your state, and render will work as expected.

Also, you should remove the this.state.output += 'w'; from the componentWillMount lifecycle. You should NOT do any state-changing operations in this lifecycle method, as it will not trigger a re-render.

componentWillMount() is invoked immediately before mounting occurs. It is called before render(), therefore setting state in this method will not trigger a re-render. Avoid introducing any side-effects or subscriptions in this method.

bamtheboozle
  • 5,847
  • 2
  • 17
  • 31
  • `You should NOT do any state-changing operations in this lifecycle method, as it will trigger an endless re-render loop and crash your application.` This is incorrect – Shubham Khatri Feb 07 '18 at 07:23
  • @DragoşPaulMarinescu Thanks for your answer. That inspired me to do some small experiments. I tried to replace this.state.output statement with setState call in each lifecycle method, and observed the following findings: (1) such a change did not affect componentWillMount(); both did work; (2) we could not use setState() in render(), which produced an error where maximun update depth was exceeded; however, this.state.output worked just fine; (3) in componentDidMount(), just as you said, this.state.output was not correct and only this.setState() worked so that I saw 'cwrdr'. – phr.phr Feb 07 '18 at 09:57
1

First of all you should not mutate the state directly. According to the docs:

Never mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.

Secondly: The reason you don't see 'cwrdr' in render is because this.setState() no longer triggers a re-render from v16 onwards. From the blog post

Calling setState with null no longer triggers an update. This allows you to decide in an updater function if you want to re-render.

Lastly: Don't setState in componentWillMount synchronously, do it in constructor instead.

componentWillMount() is invoked immediately before mounting occurs. It is called before render(), therefore setting state in this method will not trigger a re-render. Avoid introducing any side-effects or subscriptions in this method.

Also avoid using componentWillMount as much as possible since from v16.3.0 onwards react has proposed to rename it and deprecate it from v17 onwards. Check this answer on where to place async Requests

Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
  • `this.setState() no longer triggers a re-render from v16 onwards`. That's not true. It no longer triggers a re-render if you call it with `null`. That's different. – bamtheboozle Feb 07 '18 at 08:33
  • @DragoşPaulMarinescu, please check this codesandbox https://codesandbox.io/s/01l8nkv83p – Shubham Khatri Feb 07 '18 at 09:10