Dimitar
Dimitar

Reputation: 1180

How to trigger a callback after a redux action finishes and updates redux store?

I learnt React and Redux about an year ago and had not used them intensively until recently. I was surprised to find out that there are quite a few changes in both React and Redux.

The first thing i noticed is that I can no longer use the componentWillReceiveProps lifecycle method and its telling me i need to use some weird getDerivedStateFromProps method which doesn't really solve my current issue.

Basically, I have a register function which dispatches 3 actions - REGISTER_REQUEST, REGISTER_SUCCESS and REGISTER_FAIL. I also have 3 flags in my userReducer - registering, registered, registerError.

In my Register component, i want to do the following:

  1. When registering is true, add a loading animation
  2. When registering is false and registered is true, redirect the user to /profile route
  3. When registerError is true, get the error from the redux store and set it in the component's state.

My first approach was the following:

componentWillReceiveProps() {
    if(this.props.user){
        if(this.props.user.registered && !this.props.user.registering){
            this.props.history.push("/jobs")
        }
    }

    if(this.props.user.registerError){
        this.setState({error: this.props.user.registerErrorMessage})
    }
}

Which did not work out as I expected it to. As i mentioned above, its telling me that I am no longer allowed to use componentWillRecieveProps.

My next guess was to use the componentDidUpdate method but in that method, I can not call setState because it causes the infamous state loop error. I have worked around this error before but not without doing a lot of hacky things which I do not like.

I did end up solving this problem by creating a history helper which allowed me to redirect user from outside the component (the register function), but that ended up causing more problems with the react router and it did not solve my issue of setting the registerErrorMessage to the components state.

So what is the correct way of doing this in React and Redux? I am really trying to avoid using hacky tricks to get around these problems since almost always they have come around and bit me in the ass later on.

Upvotes: 0

Views: 596

Answers (2)

Yousaf
Yousaf

Reputation: 29282

Although i don't see a reason why would you need to save the error message in your component's state instead of just getting the error from the redux store and displaying it to the user, you can call this.setState inside componentDidUpdate function but to avoid the infinite cycle of state update and re-render, you need to update the state conditionally.

Assuming both registerError inside redux store and error in your component's state are initially null, inside componentDidUpdate function, check if this.state.error is equal to register error from the redux store or not. If they are not equal, update the state.

componentDidUpdate(prevProps, prevState) {
    if (this.state.error != this.props.registerError) {
      this.setState({ error: this.props.registerError });
    }
}

Demo:

In this demo there's a Register component which dispatches an action to fetch the user from jsonplaceholder API. When HTTP request starts, REGISTER_REQUEST action is dispatched which sets registering state in the redux store to true. If user is fetched successfully, REGISTER_SUCCESS action is dispatched. Incase of an error, REGISTER_ERROR action is dispatched. When there's an error, that error is saved in the state inside Register component and is also displayed to the user.

To produce an error while making the HTTP request, i have changed API URL from

"https://jsonplaceholder.typicode.com/users/1"     // correct URL

to

"https://jsonplaceholder.typicode.com/user/1"     // incorrect URL

Upvotes: 1

Daniel Geffen
Daniel Geffen

Reputation: 1862

When the props change due to changes in the redux store, the component is rendered again. So you should put this logic in the render function. Since calling setState will call another render, you should just use the error value from the props and not save it in the state.

Upvotes: 0

Related Questions