forked from remix-run/react-router
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTransitionUtils.js
More file actions
134 lines (118 loc) · 3.83 KB
/
TransitionUtils.js
File metadata and controls
134 lines (118 loc) · 3.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import { loopAsync } from './AsyncUtils'
class PendingHooks {
hooks = []
add = hook => this.hooks.push(hook)
remove = hook => this.hooks = this.hooks.filter(h => h !== hook)
has = hook => this.hooks.indexOf(hook) !== -1
clear = () => this.hooks = []
}
export default function getTransitionUtils() {
const enterHooks = new PendingHooks()
const changeHooks = new PendingHooks()
function createTransitionHook(hook, route, asyncArity, pendingHooks) {
const isSync = hook.length < asyncArity
const transitionHook = (...args) => {
hook.apply(route, args)
if (isSync) {
let callback = args[args.length - 1]
// Assume hook executes synchronously and
// automatically call the callback.
callback()
}
}
pendingHooks.add(transitionHook)
return transitionHook
}
function getEnterHooks(routes) {
return routes.reduce(function (hooks, route) {
if (route.onEnter)
hooks.push(createTransitionHook(route.onEnter, route, 3, enterHooks))
return hooks
}, [])
}
function getChangeHooks(routes) {
return routes.reduce(function (hooks, route) {
if (route.onChange)
hooks.push(createTransitionHook(route.onChange, route, 4, changeHooks))
return hooks
}, [])
}
function runTransitionHooks(length, iter, callback) {
if (!length) {
callback()
return
}
let redirectInfo
function replace(location) {
redirectInfo = location
}
loopAsync(length, function (index, next, done) {
iter(index, replace, function (error) {
if (error || redirectInfo) {
done(error, redirectInfo) // No need to continue.
} else {
next()
}
})
}, callback)
}
/**
* Runs all onEnter hooks in the given array of routes in order
* with onEnter(nextState, replace, callback) and calls
* callback(error, redirectInfo) when finished. The first hook
* to use replace short-circuits the loop.
*
* If a hook needs to run asynchronously, it may use the callback
* function. However, doing so will cause the transition to pause,
* which could lead to a non-responsive UI if the hook is slow.
*/
function runEnterHooks(routes, nextState, callback) {
enterHooks.clear()
const hooks = getEnterHooks(routes)
return runTransitionHooks(hooks.length, (index, replace, next) => {
const wrappedNext = (...args) => {
if (enterHooks.has(hooks[index])) {
next(...args)
enterHooks.remove(hooks[index])
}
}
hooks[index](nextState, replace, wrappedNext)
}, callback)
}
/**
* Runs all onChange hooks in the given array of routes in order
* with onChange(prevState, nextState, replace, callback) and calls
* callback(error, redirectInfo) when finished. The first hook
* to use replace short-circuits the loop.
*
* If a hook needs to run asynchronously, it may use the callback
* function. However, doing so will cause the transition to pause,
* which could lead to a non-responsive UI if the hook is slow.
*/
function runChangeHooks(routes, state, nextState, callback) {
changeHooks.clear()
const hooks = getChangeHooks(routes)
return runTransitionHooks(hooks.length, (index, replace, next) => {
const wrappedNext = (...args) => {
if (changeHooks.has(hooks[index])) {
next(...args)
changeHooks.remove(hooks[index])
}
}
hooks[index](state, nextState, replace, wrappedNext)
}, callback)
}
/**
* Runs all onLeave hooks in the given array of routes in order.
*/
function runLeaveHooks(routes, prevState) {
for (let i = 0, len = routes.length; i < len; ++i)
if (routes[i].onLeave)
routes[i].onLeave.call(routes[i], prevState)
}
return {
runEnterHooks,
runChangeHooks,
runLeaveHooks
}
}