UNPKG

2.53 kBJavaScriptView Raw
1// Constants
2
3export const TRANSITION = '@@router/TRANSITION'
4export const UPDATE_LOCATION = '@@router/UPDATE_LOCATION'
5
6const SELECT_STATE = state => state.routing
7
8function transition(method) {
9 return arg => ({
10 type: TRANSITION,
11 method, arg
12 })
13}
14
15export const routeActions = {
16 push: transition('push'),
17 replace: transition('replace'),
18 go: transition('go'),
19 goBack: transition('goBack'),
20 goForward: transition('goForward')
21}
22
23function updateLocation(location) {
24 return {
25 type: UPDATE_LOCATION,
26 location
27 }
28}
29
30// Reducer
31
32const initialState = {
33 location: undefined
34}
35
36export function routeReducer(state = initialState, { type, location }) {
37 if (type !== UPDATE_LOCATION) {
38 return state
39 }
40
41 return { ...state, location }
42}
43
44// Syncing
45
46export function syncHistory(history) {
47 let unsubscribeHistory, currentKey, unsubscribeStore
48 let connected = false, syncing = false
49
50 function middleware(store) {
51 unsubscribeHistory = history.listen(location => {
52 currentKey = location.key
53 if (syncing) {
54 // Don't dispatch a new action if we're replaying location.
55 return
56 }
57
58 store.dispatch(updateLocation(location))
59 })
60
61 connected = true
62
63 return next => action => {
64 if (action.type !== TRANSITION || !connected) {
65 return next(action)
66 }
67
68 const { method, arg } = action
69 history[method](arg)
70 }
71 }
72
73 middleware.listenForReplays =
74 (store, selectRouterState = SELECT_STATE) => {
75 const getRouterState = () => selectRouterState(store.getState())
76 const { location: initialLocation } = getRouterState()
77
78 unsubscribeStore = store.subscribe(() => {
79 const { location } = getRouterState()
80
81 // If we're resetting to the beginning, use the saved initial value. We
82 // need to dispatch a new action at this point to populate the store
83 // appropriately.
84 if (!location) {
85 history.transitionTo(initialLocation)
86 return
87 }
88
89 // Otherwise, if we need to update the history location, do so without
90 // dispatching a new action, as we're just bringing history in sync
91 // with the store.
92 if (location.key !== currentKey) {
93 syncing = true
94 history.transitionTo(location)
95 syncing = false
96 }
97 })
98 }
99
100 middleware.unsubscribe = () => {
101 unsubscribeHistory()
102 if (unsubscribeStore) {
103 unsubscribeStore()
104 }
105
106 connected = false
107 }
108
109 return middleware
110}