Search code examples
reactjsreduxreact-routerredux-thunk

how to do history.push to another page after dispatch a signup success action in redux-thunk


I am using redux-thunk recently, and meet such case:

In signup action creator function, i wanna do history.push after i dispatch a signup success action, the code may look like this:

// SignupAction.js

export const signupRequest = (userData) => {
  return dispatch => {
    dispatch(signupPending())

    return axios.post('/api/user', userData)
      .then(res => dispatch(signupSuccess()))
      .then(() => {
        // wanna do history.push('/') to the home page after successfully signup
      })
      .catch(error => dispatch(signupFailure(error.message)))
  }
}

Right now i am confused, I am trying to put all the logic into one action, but there is no way to inject the history into my signup action. I can give the signupRequest the function a second argument, it could be the history object itself: signupRequest(userData, history), or i can just pass a callback: signupRequest(userData, callback). However i am not sure which one is better.

There's also an alternative way to get history, i don't need put all logic in a single action, so the signup action would just simply return a promise, and i will deal with it later in the component, in this case, accessing the history would be quite straightforward:

// signupAction.js

export const signupRequest = (userData) => {
  return dispatch => {
    return axios.post('/api/users', userData);
  }
}

// signupForm.js
export default class SignupForm extends Component {
  // ...
  handleSubmit = e => {
    const userData = e.target.value
    this.props.signupRequest(userData)
      .then(() => this.props.dispatch(signupSuccess()))
      .then(() => {
        // now i can easily access history inside the component
        this.props.history.push('/')
      })
      .catch(error => dispatch(signupFailure(error.message)))  }
}

So which way should i follow, and is there any best practice to solve this problem?


Solution

  • Not sure if this is the best practice, but i see some people do it like this: solution

    The key is to put history in a single file so that we can reference it and use it outside the component:

    First create a history object in a standalone file:

    // history.js
    import { createBrowserHistory } from 'history';
    
    export default createBrowserHistory();
    

    Then wrap it in the Router component, be sure to use Router rather than BrowserRouter:

    import { Router, Route } from 'react-router-dom';
    import history from './history';
    
    ReactDOM.render(
      <Router history={history}>
        ...
      </Router>
    )
    

    Finally in redux actionCreators we are able to import the history object and use it:

    // SignupAction.js
    import history from './history'
    
    export const signupRequest = (userData) => {
      return dispatch => {
        dispatch(signupPending())
    
        return axios.post('/api/user', userData)
          .then(res => dispatch(signupSuccess()))
          .then(() => {
            // we are reference/use the same `history` all the time by import it
            history.push('/')
          })
          .catch(error => dispatch(signupFailure(error.message)))
      }
    }
    

    So basically i still prefer to put logic inside the actionCreator, the component should only focus on UI rendering, the logic should be dealt by redux instead.