Search code examples
reactjsreduxreact-reduxreact-lifecycle

ComponentDidMount not getting called after redux state update?


I think I'm missing a concept here about React and Redux. I'm trying to work with objects stored in redux, and I'm having trouble.

REDUX: I have an action fetchItems, that gets all items from the database. This action works successfully.

REACT: I have a container, UserProfile, that calls fetchItems in componentDidMount.

class UserProfile extends Component {

  componentWillMount() {
    console.log('------------ USER PROFILE -------------------');
  }

  componentDidMount() {
    console.log('[ComponentDidMount]: Items: ', this.props.items);
    this.props.fetchItems();
  }

  render() {
    let profile = null;
    console.log('[Render]: Items: ', this.props.items);

    return <Auxillary>{profile}</Auxillary>;
  }
}

const mapStateToProps = state => {
  return {
    items: state.items.items
  };
};

const mapDispatchToProps = dispatch => {
  return {
    fetchItems: () => dispatch(actions.fetchItems())
  };
};

export default connect(mapStateToProps, mapDispatchToProps)(UserProfile);

The problem I'm seeing is that this.props.items is always null (even though fetchItems is successful). The only way I can detect that items were stored in redux store is if I use componentWillRecieveProps(nextProps). Here, I successfully see the items in nextProps. I feel like using componentWillReceiveProps might be too "messy" though. I guess what I'm asking is, what is the standard way of dealing with updates to redux states in react?

Aseel


Solution

  • In react, we have something called state. if the state of a component is changed the component will re-render. Having said that we can use this.setState() inside componentWillRecieveProps to update the state which in turn will rerender the component. So your code will look like this which is the standard way to handle Redux level state changes in react.

    class UserProfile extends Component {
    
        constructor(props) {
            super(props);
            this.state = {
                items: props.items
            }
        }
        componentWillMount() {
          console.log('------------ USER PROFILE -------------------');
        }
    
        componentWillRecieveProps({ items }) {
            this.setState({ items });
        }
    
        componentDidMount() {
          console.log('[ComponentDidMount]: Items: ', this.state.items);
          this.props.fetchItems();
        }
    
        render() {
          let profile = null;
          console.log('[Render]: Items: ', this.state.items);
    
          return <Auxillary>{profile}</Auxillary>;
        }
      }
    
      const mapStateToProps = state => {
        return {
          items: state.items.items
        };
      };
    
      const mapDispatchToProps = dispatch => {
        return {
          fetchItems: () => dispatch(actions.fetchItems())
        };
      };
    
      export default connect(mapStateToProps, mapDispatchToProps)(UserProfile);
    

    P.S Just making the API call inside componentWillMount will not help either as API call is async and can take up some time to resolve and till then react will finish rendering the component. so you'll still have to use componentWillRecieveProps