diff --git a/CHANGES.md b/CHANGES.md
index ff5a472b85..f8e7818841 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,7 +1,10 @@
## [HEAD]
> Unreleased
-- Nothing yet!
+- Fixed bug where transition hooks were not called on child routes of
+ parent's whose params changed but the child's did not. ([#3166])
+
+[#3166][https://github.com/reactjs/react-router/pull/3166]
[HEAD]: https://github.com/reactjs/react-router/compare/v2.0.0...HEAD
diff --git a/modules/__tests__/transitionHooks-test.js b/modules/__tests__/transitionHooks-test.js
index 625c11600b..7a6bf792df 100644
--- a/modules/__tests__/transitionHooks-test.js
+++ b/modules/__tests__/transitionHooks-test.js
@@ -10,7 +10,7 @@ describe('When a router enters a branch', function () {
let
node,
newsLeaveHookSpy, removeNewsLeaveHook, userLeaveHookSpy,
- DashboardRoute, NewsFeedRoute, InboxRoute, RedirectToInboxRoute, MessageRoute, UserRoute,
+ DashboardRoute, NewsFeedRoute, InboxRoute, RedirectToInboxRoute, MessageRoute, UserRoute, AssignmentRoute,
routes
beforeEach(function () {
@@ -52,6 +52,12 @@ describe('When a router enters a branch', function () {
}
}
+ class UserAssignment extends Component {
+ render() {
+ return
assignment {this.props.params.assignmentId}
+ }
+ }
+
class User extends Component {
componentWillMount() {
this.context.router.setRouteLeaveHook(
@@ -61,7 +67,7 @@ describe('When a router enters a branch', function () {
}
render() {
- return User
+ return User {this.props.params.userId} {this.props.children}
}
}
@@ -121,9 +127,19 @@ describe('When a router enters a branch', function () {
}
}
+ AssignmentRoute = {
+ path: 'assignments/:assignmentId',
+ component: UserAssignment,
+ onEnter() { expect(this).toBe(AssignmentRoute) },
+ onLeave() { expect(this).toBe(AssignmentRoute) }
+ }
+
UserRoute = {
path: 'users/:userId',
- component: User
+ component: User,
+ childRoutes: [ AssignmentRoute ],
+ onEnter() { expect(this).toBe(UserRoute) },
+ onLeave() { expect(this).toBe(UserRoute) }
}
DashboardRoute = {
@@ -277,6 +293,38 @@ describe('When a router enters a branch', function () {
})
})
+ describe('and then navigates to the same branch, but with different parent params', function () {
+ it('calls the onLeave and onEnter hooks of the parent and children', function (done) {
+ const parentLeaveSpy = spyOn(UserRoute, 'onLeave').andCallThrough()
+ const parentEnterSpy = spyOn(UserRoute, 'onEnter').andCallThrough()
+ const childLeaveSpy = spyOn(AssignmentRoute, 'onLeave').andCallThrough()
+ const childEnterSpy = spyOn(AssignmentRoute, 'onEnter').andCallThrough()
+ const history = createHistory('/users/123/assignments/456')
+
+ const steps = [
+ function () {
+ expect(parentEnterSpy).toHaveBeenCalled()
+ expect(childEnterSpy).toHaveBeenCalled()
+ history.push('/users/789/assignments/456')
+ },
+ function () {
+ expect(parentLeaveSpy).toHaveBeenCalled()
+ expect(childLeaveSpy).toHaveBeenCalled()
+ expect(parentEnterSpy).toHaveBeenCalled()
+ expect(childEnterSpy).toHaveBeenCalled()
+ }
+ ]
+
+ const execNextStep = execSteps(steps, done)
+
+ render(
+ , node, execNextStep)
+ })
+ })
+
describe('and then the query changes', function () {
it('calls the onEnter hooks of all routes in that branch', function (done) {
const newsFeedRouteEnterSpy = spyOn(NewsFeedRoute, 'onEnter').andCallThrough()
diff --git a/modules/computeChangedRoutes.js b/modules/computeChangedRoutes.js
index 05c386dacc..e8a6b9e152 100644
--- a/modules/computeChangedRoutes.js
+++ b/modules/computeChangedRoutes.js
@@ -27,8 +27,16 @@ function computeChangedRoutes(prevState, nextState) {
let leaveRoutes, enterRoutes
if (prevRoutes) {
+ let parentIsLeaving = false
leaveRoutes = prevRoutes.filter(function (route) {
- return nextRoutes.indexOf(route) === -1 || routeParamsChanged(route, prevState, nextState)
+ if (parentIsLeaving) {
+ return true
+ } else {
+ const isLeaving = nextRoutes.indexOf(route) === -1 || routeParamsChanged(route, prevState, nextState)
+ if (isLeaving)
+ parentIsLeaving = true
+ return isLeaving
+ }
})
// onLeave hooks start at the leaf route.