on f7db190 help sign in
bridge/src/lib/useRouter.js
100 lines 2.5 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 replaceWith = useCallback(routes => setRoutes(routes), [setRoutes]);
59 const includes = useCallback(key => _includes(routes.map(r => r.key), key), [
60 routes,
61 ]);
62 const size = routes.length;
63 const data = useMemo(() => last(routes).data, [routes]);
64 const Route = useMemo(() => views[peek().key], [views, peek]);
66 // Scroll to top of page with each route transition
67 useEffect(() => {
68 window.scrollTo(0, 0);
69 }, [routes, primary]);
71 // capture browser pop in primary router
72 useEffect(() => {
73 if (!primary) {
74 return;
75 }
77 window.history.pushState(null, null, null);
79 window.onpopstate = e => {
80 window.history.pushState(null, null, null);
81 pop();
82 };
84 // TODO: disposer functon
85 }, [pop, primary]);
87 return {
88 Route,
89 names,
90 routes,
91 push,
92 popAndPush,
93 pop,
94 replaceWith,
95 peek,
96 size,
97 includes,
98 data,
99 };