-
Notifications
You must be signed in to change notification settings - Fork 0
/
useStoreWithRouteParams.tsx
54 lines (45 loc) · 1.57 KB
/
useStoreWithRouteParams.tsx
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
import { reaction } from 'mobx';
import qs from 'qs';
import React from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import { StoreWithRouteParams } from '../stores/StoreWithRouteParams';
/** Updates a store that implements the {@link StoreWithRouteParams} interface when a route changes. */
export const useLocationToRouteParams = <TRouteParams,>(
store: StoreWithRouteParams<TRouteParams>,
): void => {
const location = useLocation();
// Pass `location` as deps instead of `location.search`.
React.useEffect(() => {
const routeParams: any = qs.parse(location.search.slice(1));
if (store.validateRouteParams(routeParams)) {
store.popState = true;
store.routeParams = routeParams;
store.popState = false;
}
}, [location, store]);
};
/** Updates a route when a store that implements the {@link StoreWithRouteParams} interface changes. */
export const useRouteParamsToLocation = <TRouteParams,>(
store: StoreWithRouteParams<TRouteParams>,
): void => {
const navigate = useNavigate();
React.useEffect(() => {
// Returns the disposer.
return reaction(
() => store.routeParams,
(routeParams) => {
if (!store.popState) {
const newUrl = `?${qs.stringify(routeParams)}`;
navigate(newUrl);
}
},
);
}, [store, navigate]);
};
/** Updates a store that implements the {@link StoreWithRouteParams} interface when a route changes, and vice versa. */
export const useStoreWithRouteParams = <TRouteParams,>(
store: StoreWithRouteParams<TRouteParams>,
): void => {
useLocationToRouteParams(store);
useRouteParamsToLocation(store);
};