Search code examples
reactjsreact-routerreduxreact-reduxreact-router-redux

infinite loop when dispatching in componentWillReceiveProps


I have a Profile component that is loaded by react-router (path="profile/:username") and the component itself looks like this:

...
import { fetchUser } from '../actions/user';

class Profile extends Component {
  constructor(props) {
    super(props);
  }
  componentDidMount() {
    const { username } = this.props;
    this.fetchUser(username);
  }
  componentWillReceiveProps(nextProps) {
    const { username } = nextProps.params;
    this.fetchUser(username);
  }
  fetchUser(username) {
    const { dispatch } = this.props;
    dispatch(fetchUser(username));
  }
  render() {...}
}

export default connect((state, ownProps) => {
  return {
    username: ownProps.params.username,
    isAuthenticated: state.auth.isAuthenticated
  };
})(Profile);

And the fetchUser action looks like this (redux-api-middleware):

function fetchUser(id) {
  let token = localStorage.getItem('jwt');
  return {
    [CALL_API]: {
      endpoint: `http://localhost:3000/api/users/${id}`,
      method: 'GET',
      headers: { 'x-access-token': token },
      types: [FETCH_USER_REQUEST, FETCH_USER_SUCCESS, FETCH_USER_FAILURE]
    }
  }
}

The reason I added componentWillReceiveProps function is to react when the URL changes to another :username and to load that users profile info. At a first glance everything seems to work but then I noticed while debugging that componentWillReceiveProps function is called in a infinite loop and I don't know why. If I remove componentWillReceiveProps then the profile doesn't get updated with the new username but then I have no loops problem. Any ideas?


Solution

  • Your componentWillReceiveProps is in an infinite loop because calling fetchUser will dispatch an action that will update the Props.

    Add a comparison to check if the specific prop changes before dispatching the action. EDIT:

    In React 16.3+ componentWillReceiveProps will be slowly deprecated.

    It is recommended to use componentDidUpdate in place of componentWillReceiveProps

    componentDidUpdate(prevProps) {
      if (this.props.params.username !== prevProps.params.username) {
        dispatch(fetchUser(username));
      }
    }
    

    See https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#fetching-external-data-when-props-change