on 30fffab help sign in
bridge/src/lib/useRouter.js
98 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
66 useEffect(() => {
67 window.scrollTo(0, 0);
68 }, [routes, primary]);
70 // capture browser pop in primary router
71 useEffect(() => {
72 if (!primary) {
73 return;
74 }
76 window.history.pushState(null, null, null);
78 window.onpopstate = e => {
79 window.history.pushState(null, null, null);
80 pop();
81 };
83 // TODO: disposer functon
84 }, [pop, primary]);
86 return {
87 Route,
88 names,
89 routes,
90 push,
91 popAndPush,
92 pop,
93 peek,
94 size,
95 includes,
96 data,
97 };