connected-react-router icon indicating copy to clipboard operation
connected-react-router copied to clipboard

push a new route only triggers URL change but not location change

Open chuanxie opened this issue 7 years ago • 49 comments

set up the connected-react-router as doc. but it only update the url rather than the location object when yield put(push(/somewhere)) in saga. so it won't re-render the component.

try to resolve this via Update Blockingsection in react-router but it works fine outside the redux-saga. which means the push method from connected-react-router doesn't work as expected.

any suggestions?

Thanks

chuanxie avatar Oct 19 '18 18:10 chuanxie

If possible, can you check it once in redux-thunk? Is it working there? In my case also, it was not changing the location from redux-saga, so I just moved it in thunk. May be it doesn't fit your use-case, but just have a try. Ex:

const mapDispatchToProps = dispatch => {
    dispatch(push(/somewhere/))
}

akhil-gautam avatar Oct 20 '18 16:10 akhil-gautam

I have this same issue. I can confirm dispatching a push does not render anything. This is my component:

import React from "react";
import { connect } from "react-redux";
import { Switch, Route, Redirect, withRouter } from "react-router-dom";

import SideBar from "Components/SideBar";
import Dashboard from "Features/Dashboard";
import Team from "Features/Team";
import userActions from "store/user/actions";
import styles from "./styles.scss";

class AppRouter extends React.PureComponent {
    componentDidMount() {
        if (!this.props.hasUserDataLoaded) {
            this.props.loadUserData();
        }
    }

    render() {
        return (
            <div className={styles.container}>
                <SideBar pathname={this.props.location.pathname} />
                <Switch>
                    <Route path="/dashboard" Component={Dashboard} />
                    <Route path="/team" Component={Team} />
                    <Redirect to="/dashboard" />
                </Switch>
            </div>
        );
    }
}

const mapStateToProps = state => ({
    hasUserDataLoaded: state.user.hasUserDataLoaded,
});

const mapDispatchToProps = dispatch => ({
    loadUserData: () => dispatch(userActions.loadUserData()),
});

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(AppRouter));

kurt-hardy avatar Oct 22 '18 16:10 kurt-hardy

Figured my issue out. Notice how I've used title case for the component prop in the Route. Double check your not doing something stupid like me :-)

kurt-hardy avatar Oct 24 '18 09:10 kurt-hardy

Same is happening inside a saga, doing: yield put(push('/home')) just changes the URL, but it does not render the component.

My config is the one from react-boilerplate plus this pr.

fbove avatar Oct 24 '18 15:10 fbove

Does it render a default component or if you change the URL manually? My default route wasn't being rendered even after I disconnected it from the store.

If you have a repo or codepen, I can take a look.

kurt-hardy avatar Oct 25 '18 07:10 kurt-hardy

Solved Saga was cancelled after LOCATION_CHANGE.

@kurt-hardy Thanks for answering and willing to help!

fbove avatar Oct 25 '18 13:10 fbove

Hey, got the same issue here. The Links are working, but I can't push new URL.

I have a working example here : https://codesandbox.io/s/vvznjm8mz3

florianchevallier avatar Oct 28 '18 14:10 florianchevallier

Did you get that working? I see that your not using connected router, your using browser router.

kurt-hardy avatar Oct 29 '18 10:10 kurt-hardy

I go it working when doing like this : https://codesandbox.io/s/p2wp49mmp0

I didn't get that I should not use BrowserRouter. Thanks

florianchevallier avatar Oct 29 '18 10:10 florianchevallier

Your not actually using the connected-router. Using history.push() is fine but if you need to start using push inside reducers, epics or sagas, you will need to use the connected router (or come up with your own routing solution).

Take a look here if you need help getting the connected router working. https://github.com/kurt-hardy/react-router-redux-issue

Feel free to question me on this too, not that I'm an expert but I'll try. :-)

kurt-hardy avatar Oct 29 '18 10:10 kurt-hardy

Yeah that's what I thought too, I felt like cheating; I've updated the codebox :)

I was doing two things wrong :

  • Using two different instances of history
  • Using both ConnectedRouter and BrowserRouter

See if something is wrong with the codesandbox, but else, it's ok for me :)

florianchevallier avatar Oct 29 '18 10:10 florianchevallier

No everything looks good there. That's the same setup as I have it.

Glad you got it working. 👍

kurt-hardy avatar Oct 29 '18 10:10 kurt-hardy

@fbove I have same problem where my saga gets cancelled when I do yield put(push('/newlocation'). How did you solve it? Pls check my question https://stackoverflow.com/questions/53223958/saga-always-cancelled?noredirect=1#comment93342944_53223958

Absvep avatar Nov 09 '18 15:11 Absvep

My issue had to do with doing a LOCATION_CHANGE and the saga being cancelled cause of that. In fact I do agree with "Andrey Moiseev", you should check for something like

yield take(LOCATION_CHANGE)
yield cancel(watcher)

or if the component that's mounting that saga is still mounted also.

If you are doing a yield put(push('/dashboard')) and the component for that route is in a different branch of your "routing tree", I'll guess the saga won't be listening. If that's the case, you'll have to hook the saga to an upper level component, which contains the other two. Hope this makes sense.

fbove avatar Nov 09 '18 15:11 fbove

I have the same issue where LOCATION_CHANGE is cancelling my saga. But how did you solve it in detail? I also use react boilerplate and its newly added connected-react-router. I use withRouter in base App component as well, else the redirect does not happen. Please help, I've been looking for answer for 2 days:(

Absvep avatar Nov 09 '18 17:11 Absvep

I solved it by removing:

yield take(LOCATION_CHANGE)
yield cancel(watcher)

And placing the watcher on app/containers/App/saga.js as I needed it to be global and persist through location changes.

p/s: Instead of removing it, you could use an other action to cancel it.

fbove avatar Nov 09 '18 20:11 fbove

@fbove Thanks a lot man! It solved it to move saga to global scope! Thank you!

Absvep avatar Nov 10 '18 15:11 Absvep

I have a similar problem with redux-observable and I explained it in details in Stackoverflow. If anyone can help?

Galya-IT avatar Nov 13 '18 10:11 Galya-IT

I had the same problem today. It seems to break with react-dom >= 16.5. Downgrading it to 16.4.1 solved it for me.

Any Idea why?

monestereo avatar Nov 15 '18 14:11 monestereo

I had the same problem and resolved it for some days. Finally, I found no history specified in tag <Router> and Router is BrowserRouter. I change it to <Router history={history}> (Router is default, not BrowserRouter) and the history is shared with router reducer. Code as below:

history.tsx

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

store.tsx

import { routerMiddleware } from 'connected-react-router';

import history from './history';
import { createRootReducer } from './reducer';
const appRootReducer = createRootReducer(history);
const appRouterMiddleware = routerMiddleware(history);
//...

!App.tsx

//...
import { BrowserRouter as Router } from 'react-router-dom';
class AppComponent extends React.Component {
    render() {
        return (
              <Router>
                    <main>{renderRoutes(routes)}</main>
              </Router>
        );
    }
}

CHANGE TO:

//...
import { Router } from 'react-router-dom';

class AppComponent extends React.Component {
    render() {
        return (
              <Router history={history}>
                    <main>{renderRoutes(routes)}</main>
              </Router>
        );
    }
}
//...

bndynet avatar Nov 29 '18 02:11 bndynet

Ok its working on development, but I have a problem with serve the statics index the console says and didn't display anything.

Uncaught TypeError: Cannot read property 'location' of undefined

On deploy the statics and serve with express with

app.get('/*', (req, res) => {
  res.sendFile(path.join(__dirname, 'index.html'))
})

Follow these steps works well but serving not working IDK what happens.

My dispatch

fizzvr avatar Dec 24 '18 17:12 fizzvr

Make sure in component you are declare routes, wrapped with withRouter:

export default withRouter(
  connect(
    mapStateToProps,
    mapDispatchToProps,
  )(App),
);

theruziev avatar Jan 03 '19 12:01 theruziev

Was having this issue. In case it may help someone, here's what caused the issue for me:

First: Actions weren't dispatching. Issue was that I was using BrowserRouter, I didn't know about ConnectedRouter.

Second: Got actions to dispatch and URL would update, but the page wouldn't change. Issue was that I needed to add the exact prop on every Route.

rm-rf-etc avatar Jan 25 '19 09:01 rm-rf-etc

Same for me: location changed, action dispatched - component not changed

addamove avatar Jan 31 '19 11:01 addamove

I go it working when doing like this : https://codesandbox.io/s/p2wp49mmp0

I didn't get that I should not use BrowserRouter. Thanks

thanks. can you refactor it with typescript please.

yash2code avatar Feb 11 '19 07:02 yash2code

Similar problem. Looked into the solutions listed above and they all stop working when you switch to the latest connected-react-router. https://codesandbox.io/s/2190y39qxy

when loaded should point to /login but points to /

CycoPH avatar Apr 26 '19 12:04 CycoPH

I have found that the error is related to notifyListeners on the history package, with a delay on the redirect this is resolved.

Bug demo repo: https://github.com/navarroaxel/react-history-redirect

navarroaxel avatar May 27 '19 19:05 navarroaxel

I was using sagas to change the location. First I was using the push method from connected react router within a saga. I tried absolutely everything to make my route change. The URL would change but the component wouldn't. I found removing the BrowserRouter component fixed my issue. Guessing it's because I was using a switch and a browserRouter component.

<ConnectedRouter history={history}>
    <BrowserRouter> {/* Removing this component solved my issue. Possibly because it's using it's own history instance. I dunno. React + it's packages change too quickly for me to keep up and stay sane at the same time, given up caring... */}
      <Switch>
        <PrivateRouter exact path="/" component={App} />
        <Route exact path="/login" component={Login} />
        <Route component={NotFound} />
     </Switch>
   <BrowserRouter>
</ConnectedRouter>

bashleigh avatar Jul 17 '19 14:07 bashleigh

For everyone looking for a quick (and probably dirty) fix using BrowserRouter ... (at least it worked for me). BrowerRouter is a forceRefresh prop. You can set this true.

<BrowserRouter forceRefresh={true}></BrowserRouter

florianmartens avatar Aug 29 '19 08:08 florianmartens

Using connected-react-router v6.4.0, If I don't use a Switch then react-router doesn't trigger the route. If I use Switch, I wasn't getting any "Location_Change" events.

The solution for me was to put the Connected-Router inside the Switch

<Provider store={store}>
    <Switch>
      <ConnectedRouter history={history}>

bishopZ avatar Sep 03 '19 17:09 bishopZ

I found this issue which match pretty well my issue although I'm not using connected-react-router. I am indeed in a vicious circle where a history.push in my saga triggers the saga cancellation.

To give some context, I'm using redux-saga in a quite unusual way: in my components I run this kind of effect:

useEffect(() => {
  const task = sagaMiddleware.run(mySaga)

  return () => {
      if (task.isRunning()) {
        task.cancel()
      }
    }
}, [])

because I want to be sure my saga is properly attached to a component. But this leads to bugs when the saga triggers the component to unmount.

At first I wrote in my saga history.push('/some-redirection') but this lead to an error: "Generator is already running" (see https://github.com/redux-saga/redux-saga/issues/703).

As advised in the linked issue, I changed my code to call({context: history, fn: history.push}, '/some-redirection') but this leads to the problem described here.

Since an action in the saga that makes the component unmount seems to be a valid case to me, I don't want to remove the code that cancel the saga on unmount.

My solution to this issue is to spawn the history.push.

export const historyPushFromSaga = (path: string) =>
  spawn(() => history.push(path))

Interestingly spawn(function*() { yield call({context: history, fn: history.push}, '/some-redirection')}) still give me a white screen. But I didn't investigate why. Instead I just call directly history.push.

nicgirault avatar Oct 10 '19 07:10 nicgirault

Had the same issue, @bashleigh has helped. It looks like it is not related to any specific middleware used - or we are dealing with two distinct problems here and we should split the issue.

As far as removing router part is concerned it is either a bug or it is intended to be used this way. If it is the latter we ought to change documentation cause it takes too much time to find this issue and a working solution.

Bielik20 avatar Nov 16 '19 13:11 Bielik20

Just wanted to share that I had this issue and what fixed it was updating to react-router@5 and react-router-dom@5.

facingmonday avatar Nov 19 '19 21:11 facingmonday

I have this same issue. I can confirm dispatching a push does not render anything. This is my component:

import React from "react";
import { connect } from "react-redux";
import { Switch, Route, Redirect, withRouter } from "react-router-dom";

import SideBar from "Components/SideBar";
import Dashboard from "Features/Dashboard";
import Team from "Features/Team";
import userActions from "store/user/actions";
import styles from "./styles.scss";

class AppRouter extends React.PureComponent {
    componentDidMount() {
        if (!this.props.hasUserDataLoaded) {
            this.props.loadUserData();
        }
    }

    render() {
        return (
            <div className={styles.container}>
                <SideBar pathname={this.props.location.pathname} />
                <Switch>
                    <Route path="/dashboard" Component={Dashboard} />
                    <Route path="/team" Component={Team} />
                    <Redirect to="/dashboard" />
                </Switch>
            </div>
        );
    }
}

const mapStateToProps = state => ({
    hasUserDataLoaded: state.user.hasUserDataLoaded,
});

const mapDispatchToProps = dispatch => ({
    loadUserData: () => dispatch(userActions.loadUserData()),
});

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(AppRouter));

wow this solved my issue. Was using BrowserRouter AND connected router. Didn't put 2 and 2 together! Thank you!!

droconnel22 avatar Nov 26 '19 02:11 droconnel22

Solved Saga was cancelled after LOCATION_CHANGE.

@kurt-hardy Thanks for answering and willing to help!

can you please explain how you solved this ?

PratapAkshay avatar Mar 25 '20 13:03 PratapAkshay

Hello everyone, In case someone is struggling with this, none of the above worked for me. Finally I've reinstalled the packages and it started to work.

yarn remove react-router-dom @types/react-router-dom yarn add react-router-dom @types/react-router-dom

hasan-aa avatar Jul 10 '20 15:07 hasan-aa

I go it working when doing like this : https://codesandbox.io/s/p2wp49mmp0

I didn't get that I should not use BrowserRouter. Thanks


This method worked very well

evey201 avatar Aug 12 '20 13:08 evey201

Same issue , just URL chnages but page does't

Mwaseemzakir avatar Aug 14 '20 08:08 Mwaseemzakir

As per this SO answer:

Changing from this:

    <Switch>
      <Route path="/">
        <Welcome />
      </Route>
      <Redirect to="/" />
    </Switch>

To this:

    <Switch>
      <>
        <Route path="/">
          <Welcome />
        </Route>
        <Redirect to="/" />
      </>
    </Switch>

... worked for me.

I was trying to implement a basic fallback redirect for any paths not explicitly specified, so random paths like http://localhost:3000/askfjasdf would redirect to http://localhost:3000. For some reason adding the fragment as a top level child of <Switch> did the trick.

adamcaron avatar Aug 14 '20 17:08 adamcaron

Just try around and this work for me.

<Provider store={store}>
    <ConnectedRouter history={history}>
      <BrowserRouter> {/* Add this */
        <Switch>
          <Route exact={true} path={navigation.login.path} component={Login} />
          <Route path={navigation.posts.path} component={Posts} />
        </Switch>
      </BrowserRouter>
    </ConnectedRouter>
  </Provider>

linhtran-agilityio avatar Dec 20 '20 15:12 linhtran-agilityio

For me it worked to set exact to true:

ReactDOM.render(
  <Router>
    <Switch>
      <Route exact path="/">
        <RecipePicker />
      </Route>
      <Route exact path="/recipesettings">
        <RecipeSettings />
      </Route>
      <Route exact path="/recipeguide" component={RecipeGuide}/>
      <Route>Page not found</Route>
    </Switch>
  </Router>,
  document.getElementById('root')
);

DutchJelly avatar Dec 28 '20 16:12 DutchJelly

My advice is use Router, not BrowserRouter or anything else, just Router, and do () => return createHistoryBrowser(); in separate file then also in the Route Tags do <Route exact path='' component={DONE()}/>. then import history from you're comps folder, then just history.push() because doesn't need called already an object.

Marcus2810 avatar Jul 09 '21 09:07 Marcus2810

Using "react-router-dom": "^6.2.1", "react": "^17.0.2" and "connected-react-router": "^6.9.2". But using push('/') is not working. Any advice?

radulfr96 avatar Dec 30 '21 06:12 radulfr96

You’re router is wrong then, either you haven’t setup history or you’re having wrapped you’re routs in Switch

On Thu, 30 Dec 2021 at 06:31, Wade Russell @.***> wrote:

Using "react-router-dom": "^6.2.1", "react-router": "^5.0.0", "react": "^17.0.2" and '"connected-react-router": "^6.9.2"'. But using push('/') is not working. Any advice?

— Reply to this email directly, view it on GitHub https://github.com/supasate/connected-react-router/issues/159#issuecomment-1002891970, or unsubscribe https://github.com/notifications/unsubscribe-auth/AOVTE3UKA7B2PYTIWBUJJRTUTP4F7ANCNFSM4F6E44PA . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

You are receiving this because you commented.Message ID: @.***>

Marcus2810 avatar Dec 31 '21 14:12 Marcus2810

*Haven’t

On Fri, 31 Dec 2021 at 14:28, marcus rose @.***> wrote:

You’re router is wrong then, either you haven’t setup history or you’re having wrapped you’re routs in Switch

On Thu, 30 Dec 2021 at 06:31, Wade Russell @.***> wrote:

Using "react-router-dom": "^6.2.1", "react-router": "^5.0.0", "react": "^17.0.2" and '"connected-react-router": "^6.9.2"'. But using push('/') is not working. Any advice?

— Reply to this email directly, view it on GitHub https://github.com/supasate/connected-react-router/issues/159#issuecomment-1002891970, or unsubscribe https://github.com/notifications/unsubscribe-auth/AOVTE3UKA7B2PYTIWBUJJRTUTP4F7ANCNFSM4F6E44PA . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

You are receiving this because you commented.Message ID: @.***>

Marcus2810 avatar Dec 31 '21 14:12 Marcus2810

Here is a sample of my code. I do have a switch at the moment.

ReactDOM.render(
    <React.StrictMode>
        <Provider store={store}>
            <OidcProvider store={store} userManager={userManager}>
                <AppContextProvider>
                    <ThemeProvider theme={theme}>
                        <SnackbarProvider maxSnack={10}>
                            <Layout>
                                <ConnectedRouter history={history}>
                                    <Switch>
                                        <Route exact path="/" component={Home} />
                                        <Route path="/callback" component={CallbackPage} />
                                        <Route path="/user" component={Users} />
                                        <Route path="/login" component={Login} />
                                        <Route path="/register" component={Register} />
                                        <Route path="/account" component={MyAccount} />
                                        <Route path="/userdetails/:id" component={UserPage} />
                                    </Switch>
                                </ConnectedRouter>
                            </Layout>
                        </SnackbarProvider>
                    </ThemeProvider>
                </AppContextProvider>
            </OidcProvider>
        </Provider>
    </React.StrictMode>,
    document.getElementById('root'),
);

radulfr96 avatar Jan 02 '22 05:01 radulfr96

In you're routes use exact Path instead of just path because you're only doing it in one of them, also make sure history is createBrowserHistory() and also Change connectedRouter to BrowerRouter or Router because these are the normal ones.

On Sun, 2 Jan 2022 at 05:07, Wade Russell @.***> wrote:

Here is a sample of my code. I do have a switch at the moment.

<React.StrictMode> <Provider store={store}> <OidcProvider store={store} userManager={userManager}> <AppContextProvider> <ThemeProvider theme={theme}> <SnackbarProvider maxSnack={10}> <Layout> <ConnectedRouter history={history}> <Switch> <Route exact path="/" component={Home} /> <Route path="/callback" component={CallbackPage} /> <Route path="/user" component={Users} /> <Route path="/login" component={Login} /> <Route path="/register" component={Register} /> <Route path="/account" component={MyAccount} /> <Route path="/userdetails/:id" component={UserPage} /> </Switch> </ConnectedRouter> </Layout> </SnackbarProvider> </ThemeProvider> </AppContextProvider> </OidcProvider> </Provider> </React.StrictMode>

— Reply to this email directly, view it on GitHub https://github.com/supasate/connected-react-router/issues/159#issuecomment-1003665003, or unsubscribe https://github.com/notifications/unsubscribe-auth/AOVTE3QGXZ6XO222MEHOJHTUT7MQLANCNFSM4F6E44PA . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

You are receiving this because you commented.Message ID: @.***>

Marcus2810 avatar Jan 02 '22 12:01 Marcus2810

Okay so could you please try doing exact path for all the ones except for default, so ‘exact path=/login’, Because you have done it for only you’re Home component.

On Sun, 2 Jan 2022 at 12:33, marcus rose @.***> wrote:

In you're routes use exact Path instead of just path because you're only doing it in one of them, also make sure history is createBrowserHistory() and also Change connectedRouter to BrowerRouter or Router because these are the normal ones.

On Sun, 2 Jan 2022 at 05:07, Wade Russell @.***> wrote:

Here is a sample of my code. I do have a switch at the moment.

<React.StrictMode> <Provider store={store}> <OidcProvider store={store} userManager={userManager}> <AppContextProvider> <ThemeProvider theme={theme}> <SnackbarProvider maxSnack={10}> <Layout> <ConnectedRouter history={history}> <Switch> <Route exact path="/" component={Home} /> <Route path="/callback" component={CallbackPage} /> <Route path="/user" component={Users} /> <Route path="/login" component={Login} /> <Route path="/register" component={Register} /> <Route path="/account" component={MyAccount} /> <Route path="/userdetails/:id" component={UserPage} /> </Switch> </ConnectedRouter> </Layout> </SnackbarProvider> </ThemeProvider> </AppContextProvider> </OidcProvider> </Provider> </React.StrictMode>

— Reply to this email directly, view it on GitHub https://github.com/supasate/connected-react-router/issues/159#issuecomment-1003665003, or unsubscribe https://github.com/notifications/unsubscribe-auth/AOVTE3QGXZ6XO222MEHOJHTUT7MQLANCNFSM4F6E44PA . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

You are receiving this because you commented.Message ID: @.***>

Marcus2810 avatar Jan 03 '22 21:01 Marcus2810

try this method https://github.com/skiller70/reduxRouter/blob/master/reduxRouting.js

skiller70 avatar Aug 08 '22 07:08 skiller70