redux-react-route v4/v5 push() is not working inside thunk action

Viewed 1843

In index.js push directly or throw dispatch works well:

...
import { push } from 'react-router-redux'

const browserHistory = createBrowserHistory()
export const store = createStore(
  rootReducer,
  applyMiddleware(thunkMiddleware, routerMiddleware(browserHistory))
)
// in v5 this line is deprecated
export const history = syncHistoryWithStore(browserHistory, store)

history.push('/any') // works well
store.dispatch(push('/any')) // works well

ReactDOM.render((
  <Provider store={store}>
    <Router history={history}>
      <App />
    </Router>
  </Provider>
), document.getElementById('root'))

App.js

class App extends Component {
  render() {
    return (
      <div className="app">
        <Switch>
          <Route path="/" component={Main} />
          <Route path="/any" component={Any} />
        </Switch>
      </div>
    );
  }
}
export default withRouter(connect(/*...*/)(App))

but in redux-thunk action all attempts ends by rewriting url, but without re-rendering

...
export function myAction(){
  return (dispatch) => {
    // fetch something and then I want to redirect...
    history.push('/any') // change url but not re-render
    dispatch(push('/any')) // change url but not re-render
    store.dispatch(push('/any')) // change url but not re-render
  }
}

This myAction is calling fetch() inside and should redirect after success.

If I run this.props.history.push('/any') inside component, it works! but I need to run redirect inside thunk action after successful fetch()

I was trying wrap all components with withRouter or Route, but didn't help.

3 Answers

Well, let me then submit another not perfect solution by passing the history object in the dispatch to the action. I guess it's more a beginners-solution but is IMHO simple to understand (and therefore simple to maintain which is the most important thing in software-development)

Using <BrowserRouter> makes all React-compoments having the history in their props. Very convenient. But, as the problem description stated, you want it outside a React Component, like an action on Redux-Thunk.

Instead of going back to <Router> I chose to stick to BrowserRouter.

  • The history object cannot be accessed outside React Components
  • I did not like going back to <Router> and using something like react-router-redux

Only option left is to pass along the history object to the action.

In a Auth-ForgotPassword component:

const submitHandler = (data) => {
   dispatch(authActions.forgotpassword({data, history:props.history}));
}

In the action function

export const forgotpassword = ({forgotpasswordData, history}) => {
   return async dispatch => {
        const url = settings.api.hostname + 'auth/forgotpassword'; // Go to the API
        const responseData = await fetch(
            url,
            {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'Accept': 'application/json',
                },
                body: JSON.stringify(forgotpasswordData),
            }
        );
        history.push('/auth/forgotpassword/success');
    }
}

And now we all wait for the final elegant solution :-)

Related