2

In my React/Redux application, I have some async actions. Let's say a user initiates a getData request to the server. Immediately a GET_DATA_REQUEST is being dispatched and the getData AJAX call is on its way to the server.

Upon success or failure, a GET_DATA_SUCCESS or GET_DATA_FAILURE actions are dispatched accordingly and the data is rendered to the UI.

Now, I want my application to push history state (using react-router-redux) as a reaction to the AJAX callback. Meaning, upon success, the users is "redirected" to another URL (route) the displays a different module that depends on the newly received data.

I realize it's a very bad idea to have this functionality in the reducer, as it won't be pure anymore (URL change is a side effect).

Any thoughts?

Thanks

Gilad Artzi
  • 3,034
  • 1
  • 15
  • 23
  • My answer here may help you out: http://stackoverflow.com/a/36269830/728013 - it puts redirect logic into your action instead, allowing your reducers to stay functional. – Cameron Sep 08 '16 at 13:20

2 Answers2

9

I believe this is a good way to handle your situation.

First of all, you should add a new property in your reducer to know if you want to redirect or not.

Something like this

const initialState = {
   ...
   redirect : false // You could use a String with the new url instead of true/false
   ....
}

switch ...
case GET_DATA_SUCCESS:
       return {
            ...state,
            redirect:true,
       }
case GET_DATA_FAILURE;
      return {
          ...state,
          redirect:false
      }

Then, in the component connected to your reducer, you should check the value of "redirect" in the componentDidUpdate function.

componentDidUpdate(){
        let {redirect} = this.props.yourReducerState;
        if(redirect === true){
            this.context.router.push("new-url");
        }
    }

Finally, you should reset "redirect" on componentWillUnmount

Hope it helps!

QoP
  • 27,388
  • 16
  • 74
  • 74
7

Another great way to do this. I learned this from this Udemy course, and I 100% recommend it.

Inside the component (a form you want to submit), put this form submit event handler, which will invoke the action.

submit(values) {
    this.props.xxxActionCreator(() => {
        this.props.history.push("/");//history is provided by react-route, .push("/") will direct app back to root path.
    });
}

render() { 
    <form onSubmit={this.submit.bind(this)} >
    .... </form>

Inside the action creator, put this

export default function xxxAction(callback) {
    const request = axios.get('...url').then(() => callback()); //here the function (callback) that was passed into this.props.xxxActionCreator() will be invoked.
    //.then() is provided by promise. This line of code means the callback (which redirects you to the root path) will be invoked, once the promise (async) is resolved.

    return { type: SOME_ACTION, payload: XXX };

GitHub demo here you can find the relevant code and the whole project. Which is kindly presented by Stephen Grider, a great teacher!

This is one way that doesn't put redirect into the state tree.

shriek
  • 5,605
  • 8
  • 46
  • 75
user3076685
  • 71
  • 1
  • 2