on f1ff0cf help sign in
bridge/src/lib/useRouter.js
102 lines 2.4 kB history raw
1import { useCallback, useMemo, useState, useEffect } from 'react';
2import { last, includes as _includes } from 'lodash';
4/**
5 * @param primary whether or not this is the top-level router
6 * @param names map of string to view key, used for consumer references
7 * @param views map of view keys to react component views
8 * @param initialRoutes initial set of routes following the { key, data } format
9 */
10export default function useRouter({
11 primary = false,
12 names = {},
13 views = {},
14 initialRoutes = [],
15}) {
16 const [routes, setRoutes] = useState(initialRoutes);
18 const push = useCallback(
19 (key, data) => {
20 if (!views[key]) {
21 throw new Error(
22 `Invalid Route: ${key}. Possible Routes: ${Object.keys(views).join(
23 ', '
24 )}`
25 );
26 }
28 setRoutes(routes => [...routes, { key, data }]);
29 },
30 [setRoutes, views]
31 );
32 const popAndPush = useCallback(
33 (key, data = {}) => {
34 if (!views[key]) {
35 throw new Error(
36 `Invalid Route: ${key}. Possible Routes: ${Object.keys(views).join(
37 ', '
38 )}`
39 );
40 }
42 setRoutes(routes => [
43 ...routes.slice(0, routes.length - 1),
44 { key, data },
45 ]);
46 },
47 [setRoutes, views]
48 );
49 const pop = useCallback(
50 (count = 1) =>
51 setRoutes(routes =>
52 routes.length > 1 ? [...routes.slice(0, routes.length - count)] : routes
53 ),
54 // ^ can't pop root route
55 [setRoutes]
56 );
57 const peek = useCallback(() => last(routes), [routes]);
58 const includes = useCallback(key => _includes(routes.map(r => r.key), key), [
59 routes,
60 ]);
61 const size = routes.length;
62 const data = useMemo(() => last(routes).data, [routes]);
63 const Route = useMemo(() => views[peek().key], [views, peek]);
65 // Scroll to top of page with each route transition on primary navigation
66 useEffect(() => {
67 if (!primary) {
68 return;
69 }
71 window.scrollTo(0, 0);
72 }, [routes, primary]);
74 // capture browser pop in primary router
75 useEffect(() => {
76 if (!primary) {
77 return;
78 }
80 window.history.pushState(null, null, null);
82 window.onpopstate = e => {
83 window.history.pushState(null, null, null);
84 pop();
85 };
87 // TODO: disposer functon
88 }, [pop, primary]);
90 return {
91 Route,
92 names,
93 routes,
94 push,
95 popAndPush,
96 pop,
97 peek,
98 size,
99 includes,
100 data,
101 };