complexSandwich
complexSandwich

Reputation: 595

Unable to set state after executing componentDidMount

I have the following state variable and componentDidMount method on my component:

  state = {
    categorized_items: []
  }
  componentDidMount() {
    this.props.fetchItems()
    // I'm trying to do the following
    console.log('uncat_items: ', this.props.uncat_items)
    this.setState({categorized_items: this.props.uncat_items})
  }
  render () {....}

  function mapStateToProps({ items_reducer }) {
    return {
     uncat_items: items_reducer.uncat_items
    }
  }

The this.props.fetchItems() action method fetches a number of items and updates the uncat_items in the redux store just fine (I can see the expected values for the this.props.uncat_items variable in the render/other methods without any problem). However, what I need to do for this particular case is right after the fetchItems method is executed and it updates the uncat_items in the store, I need to call setState and update the categorized_items in the state with uncat_items. I know calling this.setState like this in the componentDidMount method is not correct, but I don't know how to do that. As you can see I tried printing out this.props.uncat_items, but that returns an empty array, even though it executed the action method and updated in the reducer properly. Can someone please give me an idea how to do it the right way? Basically, after componentDidMount executes, set the state with a particular variable from the updated redux store.

Upvotes: 0

Views: 464

Answers (2)

kasho
kasho

Reputation: 526

You can use the componentDidUpdate lifecycle method. It looks like when your component is mounting your store might still contain the default value for that piece of state. It then updates correctly in the redux store as you mentioned but the component state is not updated.

To update component state after new props are received by the component:

componentDidUpdate(prevProps){
  // compare prevProps to current props and update
  if(this.props.uncat_items !== prevProps.uncat_items) {
    this.setState({categorized_items: this.props.uncat_items})
    }
  }

As mentioned in comments, using props directly in the render method is the more ideal solution as you don’t want your component to needlessly re-render.

Upvotes: 1

Łukasz Godziejewski
Łukasz Godziejewski

Reputation: 314

The problem in your code is that you invoke an asynchronous action (this.props.fetchItems()) and then immediately invoke this.setState() - which happens before the fetchItems request finishes (thus, before props.uncat_items gets populated).

Assuming you are using newest React, depending on your particular use case, you can either:

1) Drop state and just use directly this.props.uncat_items in the render function.

2) Use static getDerivedStateFromProps to update state due to new props.

However, as mentioned in the docs, it's not really recommended, as most cases can be handled differently (e.g. by switching to a controlled component, as stated in solution #1). A longer description can be found in this article, which is also mentioned in the docs.

Upvotes: 2

Related Questions