Mihail
Mihail

Reputation: 395

ComponentDidUpdate() data from the api with new data

Being new to react, I saw a similar example but they were not clearly explained and I didn't understand how to solve this problem. I have an API, the new data is posted to the API. ComponentDidMount() will initiate the data from the API first time. I went through the documentation and saw that componentDiUpdate() will always re-render the page if the new data is added.

This is my code so far:

constructor(props){
  super(props);
  this.state = {
    newData: [],
    dataApi: this.props.getAllData() //method that is using GET_ALL_DATA actions/reducers using fetch(get)
  }
}
// it gets the data
componentDidMount() {
  this.state.dataApi
}

componentDidUpdate(prevProps, prevState){
  this.state.dataApi.then(data => {
    if(prevProps.data != this.props.data) {
      this.setState({newData: data});
    }
  }
}

ComponentDidUpdate() errors:

Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.

How can I fix this problem? by getting the new data from the API when someone makes a post request? Thanks

Upvotes: 3

Views: 7859

Answers (4)

Mihail
Mihail

Reputation: 395

Solved the problem, thanks everyone: Solution to the problem will be:

this.state = {
   allData: [],
   isSubmitted: false
}

async componentDidUpdate(prevProps, prevState){
  if(this.mounted && this.state.isSubmitted){
     const allData = await this.props.getAllData();
     this.setState({isSubmitted: false,
                  allData: allData.data })
  }

componentDidMount(){ 
   this.mounted=true
}

componentWillUnmount() {
   this.mounted = false; 
}

functionAddDataToApi(){
   // some logic
   this.setState({isSubmitted: true})
}

Upvotes: 3

Yoav Kadosh
Yoav Kadosh

Reputation: 5155

When setting the state asynchronously (for example, after a promise is resolved), it's important to make sure that your component is still mounted, otherwise, you risk setting the state on an unmounted component (which triggers the error that you are getting).

To do that, you will need to set some kind of a flag (like this.mounted in the below example):

componentDidMount() {
  this.mounted = true;
}

async componentDidUpdate() {
  const data = await someAPICall();
  if (this.mounted && !_.isEqual(this.state.data, data)) { // See comment below
    this.setState({data});
  }
}

componentWillUnmount() {
  this.mounted = false;
}

Also, when comparing the old data with the new data, you'll need to perform a deep compare (in the example above I'm using lodash isEqual(...)).

A shallow compare (i.e. this.state.data !== data) compares the references of each of the objects, which are always going to be different, regardless of the actual data, and therefore will run into an infinite loop because setState() will trigger another componentDidUpdate() and so on.

Upvotes: 0

Toby Mellor
Toby Mellor

Reputation: 8205

The componentDidMount() lifecycle method gets called once only when your component is rendered for the first time.

In your constructor(), you don't need to put a promise on your state. Instead you can remove dataApi and just call the method directly. In componentDidMount(), you'll make your API call. When the API call has finished, you can use this.setState().

The componentDidUpdate method gets called every time one of your prop or state values gets updated. As such, it is a bad idea to update state within it as you risk infinitely looping.

constructor(props){
      super(props);
      this.state = {
           newData: [], 
      };
}

// it gets the data
componentDidMount() {
    this.props.getAllData().then(data => {
        this.setState({
            newData: data,
         });
    });
}

Upvotes: 1

OttoV
OttoV

Reputation: 274

if you want to hook to props change before re-render try hooking into componentWillReceiveProps. componentWillReceiveProps will update state synchronously.

also.. code in your componentDidMount will do nothing :)

// it gets the data
componentDidMount(){
      this.state.dataApi}

try to replace it with

// it gets the data
componentDidMount() {
    this.state.dataApi.then(data => {
        this.setState({
            newData: data,
         });
    });
}

Upvotes: 0

Related Questions