Serey
Serey

Reputation: 11329

Setting State in a Callback inside of componentDidMount

I'm currently using React 16.3(React Native), written here, it suggests that I SHOULD be making any async requests inside of componentDidMount instead of componentWillMount because that will soon be deprecated.

Unfortunately I'm getting a no-op warning as I'm trying to fetch data inside of componentDidMount, setting the data returned from my axios request as my state.

Here's a snippet —

export default class MyComponent extends Component {
    state = {
      myData: []
    }

    componentDidMount() {
      axios.get('api-endpoint')
      .then(res => this.setState({ myData: res.data })
    }
    render() { return <View>...</View> }
}

and the warning —

Warning: Can only update a mounted or mounting component. 
This usually means you called setState, replaceState, or 
forceUpdate on an unmounted component. This is a no-op.

Please check the code for the MyComponent component.

Upvotes: 2

Views: 2842

Answers (3)

Serey
Serey

Reputation: 11329

Update — The reason why my component was getting unmounted because I was setting state in my parent component. When setting state in the parent component, it forced a re-rendering of the component, which trickled down the tree & dismounted in the middle of my asynchronous request.

Upvotes: 1

Gautam
Gautam

Reputation: 428

My suggestion would be to follow proper Flux. You can attach a store listener for your MyComponent in componentDidMount() as follows.

componentDidMount() {
   //call async data fecthing method here
   store.addListener('eventname', onDataReceipt);
}

Before this, you can move the state change logic to onDataReceipt method. Call the async Data fetch from componentDidMount() and dispatch an action for which a store has registered to. Then emit the event from store. Since you have already subscribed to the event in componentDidMount(), on event emission onDataReceipt() would be executed. Also dont forget to remove the listener in componentWillUnMout()

componentWillUnMount() {
   store.removeListener('eventname', onDataReceipt);
}

Flux would take care of the rest And you wouldn' thave to worry about the warning.

Upvotes: 1

Tomasz Mularczyk
Tomasz Mularczyk

Reputation: 36179

That's the problem of having asynchronous code in your components. When for example Promise resolves (might take few seconds), a user may have already navigated to another part of your application, so when Promise resolves and tries to execute setState - you get the error that you try to update unmounted component.

My suggestion is to use something like redux-thunk, redux-saga or redux-observable etc. for your asynchronous logic... However, you can do a simple check - but it is an antipattern :

export default class MyComponent extends Component {
    state = {
      myData: []
    }

    componentDidMount() {
      this.isMounted = true;

      axios.get('api-endpoint')
      .then(res => {
        if(this.isMounted) {
          this.setState({ myData: res.data })
        }
      })
    }

    componentWillUnmount() {
      this.isMounted = false;
    }
    render() { return <div>...</div> }
}

Upvotes: 4

Related Questions