-
-
Notifications
You must be signed in to change notification settings - Fork 8k
/
historyUtils.ts
77 lines (70 loc) · 2.46 KB
/
historyUtils.ts
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
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import {useEffect} from 'react';
import {useHistory} from '@docusaurus/router';
// @ts-expect-error: TODO temporary until React 18 upgrade
import {useSyncExternalStore} from 'use-sync-external-store/shim';
import {useEvent} from './reactUtils';
import type {History, Location, Action} from 'history';
type HistoryBlockHandler = (location: Location, action: Action) => void | false;
/**
* Permits to register a handler that will be called on history actions (pop,
* push, replace). If the handler returns `false`, the navigation transition
* will be blocked/cancelled.
*/
function useHistoryActionHandler(handler: HistoryBlockHandler): void {
const history = useHistory();
const stableHandler = useEvent(handler);
useEffect(
// See https://github.com/remix-run/history/blob/main/docs/blocking-transitions.md
() => history.block((location, action) => stableHandler(location, action)),
[history, stableHandler],
);
}
/**
* Permits to register a handler that will be called on history pop navigation
* (backward/forward). If the handler returns `false`, the backward/forward
* transition will be blocked. Unfortunately there's no good way to detect the
* "direction" (backward/forward) of the POP event.
*/
export function useHistoryPopHandler(handler: HistoryBlockHandler): void {
useHistoryActionHandler((location, action) => {
if (action === 'POP') {
// Maybe block navigation if handler returns false
return handler(location, action);
}
// Don't block other navigation actions
return undefined;
});
}
/**
* Permits to efficiently subscribe to a slice of the history
* See https://thisweekinreact.com/articles/useSyncExternalStore-the-underrated-react-api
* @param selector
*/
export function useHistorySelector<Value>(
selector: (history: History<unknown>) => Value,
): Value {
const history = useHistory();
return useSyncExternalStore(
history.listen,
() => selector(history),
() => selector(history),
);
}
/**
* Permits to efficiently subscribe to a specific querystring value
* @param key
*/
export function useQueryStringValue(key: string | null): string | null {
return useHistorySelector((history) => {
if (key === null) {
return null;
}
return new URLSearchParams(history.location.search).get(key);
});
}