-
Notifications
You must be signed in to change notification settings - Fork 0
/
use-location.js
99 lines (87 loc) · 2.17 KB
/
use-location.js
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
// @ts-check
import { useState, useEffect } from 'react'
function getCurrentLocation () {
return {
pathname: window.location.pathname,
search: window.location.search
}
}
/**
* @type {Array<() => void>}
*/
const listeners = []
/**
* Notifies all location listeners. Can be used if the history state has been manipulated
* in by another module. Effectifely, all components using the 'useLocation' hook will
* update.
*/
export function notify () {
listeners.forEach(listener => listener())
}
export function useLocation () {
const [{ pathname, search }, setLocation] = useState(getCurrentLocation())
useEffect(() => {
window.addEventListener('popstate', handleChange)
return () => window.removeEventListener('popstate', handleChange)
}, [])
useEffect(() => {
listeners.push(handleChange)
return () => listeners.splice(listeners.indexOf(handleChange), 1)
}, [])
function handleChange () {
setLocation(getCurrentLocation())
}
/**
* Push a new url onto the history without reloading the page.
* @param {string} url
*/
function push (url) {
window.history.pushState(null, null, url)
notify()
}
/**
* Replace the current url with the specified one.
* @param {string} url
*/
function replace (url) {
window.history.replaceState(null, null, url)
notify()
}
/**
* Helper to interrupt the default behavior when clicking on an
* anchor element. This will push the href-attribute as the new url on
* the history, without reloading the page.
* @param {React.MouseEvent<HTMLAnchorElement, MouseEvent>} event
*/
function handleClick (event) {
const { search, pathname, target } = event.currentTarget
if (!target) {
event.preventDefault()
push(`${pathname}${search}`)
}
}
/**
* Creates props that can be used in an anchor element
* @param {string} to
*
* @example
* function Link() {
* const { link } = useLocation()
* return <a {...link('/foo?bar=baz')}>link</a>
* }
*/
function link (to) {
return {
href: to,
onClick: handleClick
}
}
return {
push,
replace,
pathname,
search,
handleClick,
link
}
}