diff --git a/docs/connectRoutes.md b/docs/connectRoutes.md index bb341398..e8dbe524 100644 --- a/docs/connectRoutes.md +++ b/docs/connectRoutes.md @@ -148,14 +148,16 @@ restoration, and should be fine while developing, especially if you're using Chr * **restoreScroll** - the `restoreScroll` is a call to `redux-first-router-restore-scroll`'s restoreScroll` function, with a `shouldUpdateScroll` callback passed a single argument. See the [scroll restoration doc](./scroll-restoration.md) for more info. -* **onBeforeChange** - `onBeforeChange` is a simple function that will be called before the routes change. It's passed your standard `dispatch` and `getState` arguments -like a thunk. - * **onAfterChange** - `onAfterChange` is a simple function that will be called after the routes change. It's passed your standard `dispatch` and `getState` arguments like a thunk. +* **onBeforeChange** - `onBeforeChange` is a simple function that will be called before the routes change. It's passed your standard `dispatch` and `getState` arguments like a thunk, as well as the `action` as a third parameter. Keep in mind unlike `onAfterChange`, the action has not been dispatched yet. Therefore, +the state won't reflect it. So you need to use the action to extract URL params from the `payload`. You can use this function to efficiently short-circuit the middleware by calling `dispatch(redirect(newAction))`, where `newAction` has the matching `type` and `payload` of the route you would like to redirect to. Using `onBeforeChange` and `location.kind === 'redirect'` + `res.redirect(301, pathname)` in your `serverRender` function is the idiom here for handling redirects server-side. See [server-rendering docs](.server-rendering.md) for more info. + * **onBackNext** - `onBackNext` is a simple function that will be called whenever the user uses the browser *back/next* buttons. It's passed your standard `dispatch` and `getState` arguments like a thunk. Actions with kinds `back`, `next`, and `pop` trigger this. +* **initialDispatch** - `initialDispatch` can be set to `false` to bypass the initial dispatch, so you can do it manually, perhaps after running sagas. An `initialDispatch` function will exist in the object returned by `connectRoutes`. + * **navigators** - `navigators` is a map of of your Redux state keys to *React Navigation* navigators. Here's how you do it: diff --git a/src/connectRoutes.js b/src/connectRoutes.js index 22783f17..c92b48fc 100644 --- a/src/connectRoutes.js +++ b/src/connectRoutes.js @@ -122,7 +122,8 @@ export default ( onBeforeChange, onAfterChange, onBackNext, - restoreScroll + restoreScroll, + initialDispatch: shouldPerformInitialDispatch = true }: Options = options const scrollBehavior = restoreScroll && restoreScroll(history) @@ -142,6 +143,7 @@ export default ( let prevState = INITIAL_LOCATION_STATE // used only to pass as 1st arg to `scrollBehavior.updateScroll` if used let nextState = {} // used as 2nd arg to `scrollBehavior.updateScroll` and to change `document.title` let prevLength = 1 // used by `historyCreateAction` to calculate if moving along history.entries track + let initialDispatch = () => null const reducer = createLocationReducer(INITIAL_LOCATION_STATE, routesMap) const thunk = createThunk(routesMap, locationKey) @@ -396,15 +398,21 @@ export default ( // dispatch the first location-aware action so initial app state is based on the url on load if (!location.hasSSR || isServer()) { // only dispatch on client before SSR is setup, which passes state on to the client - const action = historyCreateAction( - currentPathname, - routesMap, - prevLocation, - history, - 'load' - ) + initialDispatch = () => { + const action = historyCreateAction( + currentPathname, + routesMap, + prevLocation, + history, + 'load' + ) - store.dispatch(action) + store.dispatch(action) + } + + if (shouldPerformInitialDispatch !== false) { + initialDispatch() + } } else { // set correct prevLocation on client that has SSR so that it will be @@ -413,7 +421,7 @@ export default ( } // update the scroll position after initial rendering of page - setTimeout(() => _updateScroll(false)) + if (typeof window !== 'undefined') setTimeout(() => _updateScroll(false)) return store } @@ -469,6 +477,7 @@ export default ( middleware, enhancer, thunk, + initialDispatch, // returned only for tests (not for use in application code) _middlewareAttemptChangeUrl, diff --git a/src/flow-types.js b/src/flow-types.js index 298f3b9b..ff87b6d8 100644 --- a/src/flow-types.js +++ b/src/flow-types.js @@ -53,6 +53,7 @@ export type Options = { onAfterChange?: (dispatch: Dispatch, getState: GetState) => void, onBackNext?: (dispatch: Dispatch, getState: GetState) => void, restoreScroll?: History => ScrollBehavior, + initialDispatch?: boolean, navigators?: { navigators: Navigators, patchNavigators: (navigators: Navigators) => void,