7

In ReactJS + Redux project, I have a method where it makes an API request. If successful, I would like to dispatch another action creator and wait until it finishes. Then when it completes, move onto the next step.

Currently, the following code does the dispatch where it makes another API call but even before the state gets updated via dispatch, it immediately executes window.location.href ='http://localhost:3005/#/Home' and then the dispatch gets completed afterwards.

So how can I wait until the dispatch(actions.updateUserInfo(userInfo.username)) gets completed before executing the next line of code, window.location.href ='http://localhost:3005/#/Home'?

Here is the action creator:

  loggingIn(userInfo) {

    var userInfoBody = {
        'username': `${userInfo.username}`,
        'password': `${userInfo.password}`
    }

    var configuration = {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(userInfoBody)
    }

    return function(dispatch) {
      fetch('https://backendserver.com:8080/creds', configuration)
      .then(response => response.json())
      .then(response => {
        //Would like to finish this dispatch completely before start executing window.location.href ='http://localhost:3005/#/Home'
        dispatch(actions.updateUserInfo(userInfo.username))
        window.location.href ='http://localhost:3005/#/Home'
      })
      .catch((error) => {
        console.log("Error: ", error)
      })
    }

Thank you in advance

5 Answers5

1

Probably the most redux-way to do this would be by introducing yet another action to which you'll react by doing your window.location.href = '...' stuff.

Since redux can be considered "single threaded" you can be sure that between each call to dispatch your state tree is fully updated.

dispatch(actions.updateUserInfo(userInfo.username))
// It's guaranteed that at this point your updateUserInfo action is handled and state tree is updated
dispatch(actions.userInfoUpdated(userInfo.username))

New action "userInfoUpdated" you would then handle in a redux middleware by doing the window.location.href = '...' thing.

WTK
  • 15,360
  • 6
  • 31
  • 42
  • Sorry but before I accept the answer and upvote, could you show an example along with the code provided in the original post for clarification? Seems like this is the best practice. –  Oct 12 '16 at 21:04
  • This is not the redux way. The redux way would be to subscribe to the store and have your subscribe listener do the `window.location.href`. – Don Rhummy Oct 12 '16 at 22:06
  • @DonRhummy why it's not the redux way? I think it's okay – niba Oct 12 '16 at 22:22
  • @niba He's missing the `subscribe` piece to it. When you dispatch, you're telling the store that an event occurred. It then tells everyone listening. This is a very clean (and the redux suggested) way to handle running some code only when a specific action is completed. – Don Rhummy Oct 12 '16 at 22:38
  • @WTK - I think that `actions.updateUserInfo(userInfo.username)` is an async thunk so it wouldn't be complete. Also, using middleware to intercept an action dispatch doesn't seem very transparent to me. – Rick Jolly Oct 13 '16 at 05:20
  • @RickJolly It doesn't matter if it's "async". Asynchronous action would have to fire another action on success to be handled by reducers, and OP didn't care about that, cared about reacting to updateUserInfo() being done handling. That being said, I like Don's answer better, using subscribe makes more sense here. – WTK Oct 14 '16 at 11:35
1

If you return the fetch promise from your thunks, then you can continue the promise chain from the caller. Modified your code assuming updateUserInfo returns the fetch promise:

return function(dispatch) {
  return fetch('https://backendserver.com:8080/creds', configuration)
  .then(response => response.json())
  .then(response => {
    //Would like to finish this dispatch completely before start executing window.location.href ='http://localhost:3005/#/Home'
    dispatch(actions.updateUserInfo(userInfo.username))
     .then(() => {
       window.location.href ='http://localhost:3005/#/Home'
     })
  })
  .catch((error) => {
    console.log("Error: ", error)
  })
}

But I'd move the call to the second thunk (updateUserInfo) into the React component since the first thunk (loggingIn) is doing too much imo. Single Responsibility Principal and all that.

Rick Jolly
  • 2,783
  • 2
  • 20
  • 30
0

A way to do this that doesn't require to manually subscribe to the store and doesn't require any middleware (as suggested by the other answers) can be the following:

Reducer:

function userReducer (state={ doneUpdating: false, userData: null }, action) {
  if (action.type === 'UPDATE_USER_INFO') { // this is the action dispatched by updateUserInfo
    return {  ...state, userData: action.payload, doneUpdating: true }
  }
}

Component:

class YourComponent {
  componentWillReceiveProps (nextProps) {
    if (!this.props.doneUpdating && nextProps.doneUpdating) {
      window.location.href ='http://localhost:3005/#/Home'
    }
  }

  // Rest of your component methods here...
}

function mapStateToProps (state) {
  return { doneUpdating: state.userReducer.doneUpdating }
}

connect(mapStateToProps)(YourComponent)

Basically, after you're done updating the state, you set a flag. You component (connected with Redux) reads that flag from the state and in componentWillReceiveProps you detect the change and react (pun intended :)) accordingly. Depending on your case, you may need to perform the same check/redirect logic in componentWillMount. This is needed if you need to dispatch you user update action when your component is not mounted already. In that case, the doneUpdating flag can become true before the component gets mounted, and componentWillReceiveProps won't be called.

fabio.sussetto
  • 6,627
  • 3
  • 23
  • 34
0

Use react-query, it has more features for fetching data and supporting your need

-1

Put your window.location.href ='http://localhost:3005/#/Home' in your subscribe handler. And make sure you subscribe for the event:

constructor(props, children)
{
    TheStore.subscribe( this.listenForDispatch.bind( this ) );
}

listenForDispatch()
{
    //Check the state is what you want
    if ( TheStore.getState().someProp == "somevalue" )
    {
        //Call it here!
        window.location.href ='http://localhost:3005/#/Home'
    }
}

EDIT: Before you start using Redux in your code, you need to read their documentation and tutorial. subscribe is one of the basic pieces of how Redux is used.

Don Rhummy
  • 20,170
  • 31
  • 134
  • 252
  • sorry but could you show an example with the code I provided in the original post? I don't seem to quiet understand what what the subscribe does and where to implement it. –  Oct 12 '16 at 21:07
  • @LyManeug Please see my edit. It won't help you to have me fully write your code If you don't understand what `subscribe` does in Redux. (The code I wrote above fully implements what you want if you understand redux) Please read the documentation first. – Don Rhummy Oct 12 '16 at 22:04
  • This doesn't answer the question and it would have the same affect as checking a Redux prop in `componentWillReceiveProps`. The op doesn't say that `actions.updateUserInfo` would set any state that could be used to determine if `updateUserInfo` has completed. – Rick Jolly Oct 13 '16 at 05:13