9
votes

The problem is that the component is being rendered twice, once with the initial state, again after the setState method in axios promise. Why is this happening and how can I solve this.

I have used both componentWillMount and componentDidMount. I, being a noob, tried hard and failed to figure out why.

export default class Dashboard extends Component{

  constructor(props) {
    super(props)
    this.state = {
      data: {
        okay: 'lul'
      }
    }
  }

  componentWillMount() {
    axios
      .get('/api/data?param1='+this.props.location.state.param1+'&param2='+this.props.location.state.param2)
      .then(res => {
        if (res.status != 401) {
          if(res.err)
            console.log('Error while retrieving: ', res.err)
          else {
            this.setState({
              data: res.data.data
            })
          }
        } else {
          console.log('Unauthorized!');
        }
      })
  }

  render() {
    return (
        <Segment inverted vertical>
          <CardContainer data={this.state.data}/>
        </Segment>
    )
  }
}

Even basic suggestions related to react / JS / general programming is highly appreciated

1
Why do you think it's a problem? If you don't want to show anything before the data has been fetched, you can check this.state.data in the render method and return null if there's no data to show yet. setState will cause the render method to be called each time. - Håken Lid
@HåkenLid The solution that you've proposed works perfectly. But how can I render the component only once with the correct data? Shall I have to write the axios request inside the constructor and then set the initial state ? - psvs
This behaviour is how react is supposed to work. It's not a bug. Calling render is really quite cheap, since react will take care of updating the dom in a efficient manner. In some cases you might want to tweak when rerendering happens. This is discussed in the react documentation here: shouldComponentUpdate In Action - Håken Lid

1 Answers

9
votes

You have a async request in componentWillMount, so before the request is completed, your component is rendered, however once the async request is successful you have a setState function call which triggers a re-render and hence your component is getting rendered twice

This is an expected behaviour.

You can check this question for more details

Use componentWillMount or componentDidMount lifecycle functions for async request in React

According to the docs

componentWillMount() is invoked just before mounting occurs. It is called before render(), therefore calling setState() synchronously in this method will not trigger an extra rendering.

This means that if you write

componentWillMount() {
   this.setState({count: 1});
}

the state will be reflected in the initial render itself and no-render is triggered. However if you have a async method then calling setState inside it might trigger an extra render if the async request gets completed after the render is already called.

To emphasize more on the fact, you must not use componentWillMount any more, since React is oncourse to remove this method from future major realease. Instead use componentDidMount.