Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lifecycle componentWillReceiveProps is called multiple times

I have been implementing Tyler McGinnis curriculum to learn react.

It's a weather app. And i'm having trouble debugging a strange behaviour. I'm pretty sure it's something silly that i'm doing or i might have missed an information piece of information.

SearchContainer is a ParentContainer,

var React = require("react");
var Search = require("../components/Search");

var SearchContainer = React.createClass({
  propTypes: {
    formType: React.PropTypes.string
  },
  contextTypes: {
    router: React.PropTypes.object.isRequired
  },
  getDefaultProps: function() {
    return {
      formType: "form"
    }
  },
  getInitialState: function() {
    return {
      city: ""
    }
  },
  handleSearchSubmit: function(e) {
    e.preventDefault();
    this.context.router.push('/forecast/' + this.state.city);
  },
  handleCityChange: function(e) {
    this.setState({
      city: e.target.value
    });
  },
  render() {
    return (
      <Search
        formType={this.props.formType}
        city={this.state.city}
        onCityChange={this.handleCityChange}
        onSearchSubmit={this.handleSearchSubmit}/>
    );
  }
})

module.exports = SearchContainer;

SearchContainer changes Context and switches to ForecastContainer,

var React = require("react");

var Forecast = require("../components/Forecast");
var Api = require("../helpers/Api");

var ForecastContainer = React.createClass({
  getInitialState: function() {
    return {
      isLoading: true,
      data: []
    }
  },
  makeRequest: function(city) {
    this.setState({
          isLoading: true,
    });
    Api.getDayForecast(city).then( function(data) {
        this.setState({
          isLoading: false,
          data: data.data.list
        });
    }.bind(this) );
  },
  componentDidMount: function() {
    this.makeRequest(this.props.params.city);
  },
  componentWillReceiveProps: function(newProps) {
    this.makeRequest(newProps.params.city);
  },
  render() {
    return (
      <Forecast isLoading={this.state.isLoading} data={this.state.data} />
    )
  }
});

module.exports = ForecastContainer;

Now here, componentWillReceiveProps is called twice. I don't understand why. Technically it should be just called once. I'm updating state in MakeRequest method. It's called second time after state change.

I'm also enclosing screenshots for better understanding of Application flow.

enter image description here

Update:

I was using React-Router version 3.0.3. Downgrading to 2.0.0 fixes it. Which is all the more strange.

like image 554
kishanio Avatar asked Apr 10 '17 06:04

kishanio


People also ask

Why is componentDidMount called multiple times?

componentDidMount() may be called multiple times if the key prop value for the component changes.

How many times componentWillMount is called?

Your component will get unmounted every time you change route, and a new one will be mounted when you change back.

Why is my component mounting twice?

The reason why this happens is an intentional feature of the React. StrictMode . It only happens in development mode and should help to find accidental side effects in the render phase.


1 Answers

I can't tell you why it is called twice, but I can tell you that it should not matter. The problem is that you're not comparing the props for what has changed. If you do this, the code will behave the way you want:

componentWillReceiveProps: function(newProps) {
  if (newProps.params.city !== this.props.params.city) {
    this.makeRequest(newProps.params.city);
  }
},

See also the official ReactJS documentation, which states (emphasis mine): https://facebook.github.io/react/docs/react-component.html#componentwillreceiveprops

Note that React may call this method even if the props have not changed, so make sure to compare the current and next values if you only want to handle changes. This may occur when the parent component causes your component to re-render.

like image 180
Lucero Avatar answered Nov 12 '22 05:11

Lucero