React-router: listenBefore/setRouteLeaveHook not being called

Created on 7 Sep 2016  路  11Comments  路  Source: ReactTraining/react-router

I'm trying to implement "ConfirmingNavigation" functionality and having trouble following current documentation. Speaking of these 2 pages in particular:

https://github.com/reactjs/react-router/blob/master/docs/guides/Histories.md
https://github.com/reactjs/react-router/blob/master/docs/guides/ConfirmingNavigation.md

It looks there are 2 ways of achieving essentially the same thing: using setRouteLeaveHook or using listenBefore method of History. It looks to me (correct me if I'm wrong) that neither work out of the box. Consider a case where you have a deeply nested form that doesn't have current route in props, but needs to prevent navigation. For simplicity router is accessed via context, essentially having this:

...
componentDidMount: function() {
  var test = function() { console.log('hello'); };
  this.context.router.listenBefore(test); // or setRouteLeaveHook
}
...
  1. setRouteLeaveHook: because the component is deeply nested, it's hard to get a hold of current route, and you need to pass a route object, not just a pathname. This makes using this method a bit complicated. In my case this method didn't invoke the callback, which might be related to https://github.com/reactjs/react-router/issues/3793
  2. listenBefore: this is not being called at all with default setup. I'm using browserHistory that is shipped with react-router (importing like require('react-router/lib/browserHistory'))
  3. listenBeforeUnload: when trying to use History directly, you need to use specific version (2.x). Maybe this needs to be mentioned in the documentation. I tried to go through API Docs of History and make it work with new version only to see an error after I set everything up correctly. With History 2.x I managed to get listenBeforeUnload working after using useBeforeUnload wrapper. listenBefore still didn't work at all. Perhaps History instance needs to be wrapped in some of those "enhancer" middlewares, it's not clearly documented at this point.

Can you please point me at the documentation or an example where this functionality is implemented with current react-router and some version of History? SO has no issues regarding this, and following examples gets me nowhere with callbacks not being called.

Most helpful comment

Oh, now I see, I didn't fully understand what passing down the route object meant. Not sure if this is the recommended way, but I found that that route object on child routes needs to be used like:

router.setRouteLeaveHook(route.childRoutes[0], this.routerWillLeave.bind(this))

Seems a bit messy, but it's now working.

All 11 comments

See the "confirming navigation" example. Use setRouteLeaveHook.

And, yes, as currently set up, you're supposed to pass down the route object. It's probably not a great API but there's not really any chance to fix that here now.

None of the methods in the docs/examples work for me. I think I have some complicated router setup. I will try to break your example to isolate.

@taion I managed to isolate the issue. It is due to a strange behavior in History. Apparently if you have listenBefore hook set before router starts, and this hook confirms that navigation is allowed, setRouteLeaveHook is never being called. I would assume from the documentation that every hook is called and only if every hook passes, the navigation occurs. I think there supposed to be some sort of an error for this case. This took me a long time to figure out. cc @mjackson

Essentially changing "confirming navigation" example to:

....
function test() { return true; }
browserHistory.listenBefore(test)

render((
  <Router history={withExampleBasename(browserHistory, __dirname)}>
    //...
  </Router>
), document.getElementById('example'))

Breaks the navigation. If test function was to return undefined everything works as expected. This becomes an issue for CoffeeScript users with implicit return statements. I think this should to be mentioned in the documentation.

@suprMax It happened to me also but the fix you suggested had a minor issue with mine.The listenBefore ensures the confirmation but only after changing the route.When i press back button the route get changed only then it shows a confirmation dialog.

@Rahulputhukkot you need to use their additional navigating away plugin thing. this is only for internal router navigation

Having the same issue here. Top level routes seem to work as expected using the setup from the example. When navigating away child routes, setRouteLeaveHook isn't being called.

For example:

  • TopLevel A (setRouteLeaveHook is set in componentDidMount())

    • ChildA1

  • TopLevel B

    • ChildB1

TopLevel A -> TopLevel B (all good, setRouteLeaveHook function is called)
ChildA1 -> TopLevel B (setRouteLeaveHook is not called )
ChildA1-> ChildB1 (setRouteLeaveHook is not called )

If feels like something is missing from the documentation/example? Or am I misunderstanding something? I also tried setting setRouteLeaveHook on the Child route with a passed-down route object, to no avail.

If it's relevant:
To create my browser history I'm doing the following:

const history = useRouterHistory(createBrowserHistory)(
                {
                    basename: baseUrl,
                }
            )

Oh, now I see, I didn't fully understand what passing down the route object meant. Not sure if this is the recommended way, but I found that that route object on child routes needs to be used like:

router.setRouteLeaveHook(route.childRoutes[0], this.routerWillLeave.bind(this))

Seems a bit messy, but it's now working.

When change in route wont trigger component unamount, for this case setRouteLeaveHook is better set in componentWillReceiveProps()) instead of componentDidMount.

@livemixlove would you be able to share more about your solution? I'm unable to get childRoutes to work.

"dependencies":
  {
    "babel-core": "6.8.0",
    "babel-loader": "6.2.4",
    "babel-polyfill": "^6.13.0",
    "babel-preset-es2015": "6.6.0",
    "babel-preset-react": "6.5.0",
    "babel-runtime": "^6.18.0",
    "classnames": "^2.2.5",
    "css-loader": "0.23.1",
    "extract-text-webpack-plugin": "1.0.1",
    "file-loader": "0.9.0",
    "font-awesome": "^4.7.0",
    "gulp": "3.9.1",
    "gulp-cachebust": "0.0.6",
    "gulp-cli": "1.2.x",
    "gulp-sass": "2.3.2",
    "gulp-webpack": "1.5.x",
    "html-loader": "0.4.3",
    "html-webpack-plugin": "2.17.0",
    "immutable": "3.8.1",
    "jquery": "^3.1.1",
    "json-loader": "0.5.4",
    "json-server": "0.8.14",
    "karma-babel-preprocessor": "^6.0.1",
    "linkifyjs": "^2.1.4",
    "lodash": "4.12.0",
    "moment": "2.14.1",
    "moment-timezone": "0.5.4",
    "node-font-awesome": "^1.0.2",
    "node-sass": "3.9.3",
    "normalize.css": "4.2.0",
    "quill": "1.1.5",
    "ramda": "^0.22.1",
    "react": "^15.4.2",
    "react-addons-pure-render-mixin": "^15.4.2",
    "react-css-modules": "3.7.6",
    "react-dom": "^15.4.2",
    "react-flip-move": "^2.7.4",
    "react-linkify": "0.1.1",
    "react-redux": "4.4.5",
    "react-router": "^2.7.0",
    "react-router-redux": "4.0.5",
    "react-text-truncate": "^0.8.3",
    "react-textarea-autosize": "^4.0.5",
    "react-toggle": "^3.0.0",
    "react-toggle-display": "^2.1.1",
    "react-visibility-sensor": "^3.9.0",
    "reactify": "1.1.1",
    "redux": "3.5.2",
    "redux-axios-api-middleware": "0.2.0",
    "redux-axios-middleware": "^4.0.0",
    "redux-form": "5.3.1",
    "redux-idle-monitor": "0.4.3",
    "redux-promise-middleware": "^4.1.0",
    "redux-thunk": "2.1.0",
    "rx-dom": "^7.0.3",
    "sass-loader": "4.0.0",
    "sockjs-client": "1.1.0",
    "stompjs": "2.3.3",
    "style-loader": "0.13.1",
    "url-loader": "0.5.7",
    "watchify": "3.7.0",
    "webpack": "1.13.1",
    "webpack-dev-server": "1.14.0",
    "webpack-encoding-plugin": "0.2.0",
    "whatwg-fetch": "1.0.0"
  },
  "devDependencies":
  {
    "babel-eslint": "^7.2.3",
    "babel-plugin-rewire": "^1.0.0",
    "babel-plugin-syntax-decorators": "^6.13.0",
    "babel-plugin-syntax-dynamic-import": "^6.18.0",
    "babel-plugin-transform-class-properties": "^6.18.0",
    "babel-plugin-transform-decorators-legacy": "^1.3.4",
    "babel-plugin-transform-es2015-modules-commonjs": "^6.24.0",
    "babel-plugin-transform-export-extensions": "^6.22.0",
    "babel-plugin-transform-runtime": "^6.15.0",
    "babel-plugin-transform-es3-member-expression-literals": "^6.5.0",
    "babel-plugin-transform-es3-property-literals": "^6.5.0",
    "babel-preset-airbnb": "^2.4.0",
    "babel-preset-es2015": "^6.6.0",
    "babel-preset-react": "^6.5.0",
    "babel-preset-latest": "^6.16.0",
    "babel-preset-stage-1": "^6.16.0",
    "babel-register": "^6.4.3",
    "cheerio": "^0.22.0",
    "connect-livereload": "^0.6.0",
    "enzyme": "2.4.1",
    "eslint": "^3.15.0",
    "eslint-config-airbnb": "^14.1.0",
    "eslint-loader": "^1.6.1",
    "eslint-plugin-css-modules": "^2.2.0",
    "eslint-plugin-import": "^2.2.0",
    "eslint-plugin-jsx-a11y": "^4.0.0",
    "eslint-plugin-react": "^6.9.0",
    "eslint-plugin-sorting": "^0.3.0",
    "express-sse": "^0.4.1",
    "fetch-mock": "4.6.1",
    "istanbul": "github:gotwarlost/istanbul#source-map",
    "istanbul-instrumenter-loader": "^0.2.0",
    "jasmine-core": "^2.4.1",
    "jasmine-enzyme": "1.1.0",
    "karma": "^0.13.22",
    "karma-chrome-launcher": "0.2.x",
    "karma-cli": "1.0.x",
    "karma-coverage": "^1.1.1",
    "karma-firefox-launcher": "0.1.x",
    "karma-jasmine": "^0.3.8",
    "karma-jasmine-ajax": "0.1.x",
    "karma-jsx-preprocessor": "0.0.9",
    "karma-junit-reporter": "^1.1.0",
    "karma-mocha-reporter": "^2.2.1",
    "karma-notify-reporter": "^1.0.1",
    "karma-phantomjs-launcher": "1.0.x",
    "karma-sinon": "^1.0.5",
    "karma-sourcemap-loader": "~0.3",
    "karma-webpack": "1.7.x",
    "npm-run-all": "^4.0.1",
    "open": "0.0.5",
    "phantomjs-prebuilt": "2.1.x",
    "prop-types": "^15.5.10",
    "react-addons-test-utils": "^15.4.1",
    "redux-logger": "^2.7.4",
    "redux-mock-store": "1.1.1",
    "request": "^2.79.0",
    "sinon": "^1.17.7",
    "temporal": "^0.5.0"
  }
Was this page helpful?
0 / 5 - 0 ratings

Related issues

alexyaseen picture alexyaseen  路  3Comments

misterwilliam picture misterwilliam  路  3Comments

hgezim picture hgezim  路  3Comments

yormi picture yormi  路  3Comments

sarbbottam picture sarbbottam  路  3Comments