1import { NavigationProp, useNavigation } from '@react-navigation/native';
2import { useCallback, useState, useEffect, useRef } from 'react';
3
4import { useExpoRouter } from '../global-state/router-store';
5
6type GenericNavigation = NavigationProp<ReactNavigation.RootParamList>;
7
8/** Returns a callback which is invoked when the navigation state has loaded. */
9export function useLoadedNavigation() {
10  const { navigationRef } = useExpoRouter();
11  const navigation = useNavigation();
12  const isMounted = useRef(true);
13  const pending = useRef<((navigation: GenericNavigation) => void)[]>([]);
14
15  useEffect(() => {
16    isMounted.current = true;
17    return () => {
18      isMounted.current = false;
19    };
20  }, []);
21
22  const flush = useCallback(() => {
23    if (isMounted.current) {
24      const pendingCallbacks = pending.current;
25      pending.current = [];
26      pendingCallbacks.forEach((callback) => {
27        callback(navigation);
28      });
29    }
30  }, [navigation]);
31
32  useEffect(() => {
33    if (navigationRef.current) {
34      flush();
35    }
36  }, [flush]);
37
38  const push = useCallback(
39    (fn: (navigation: GenericNavigation) => void) => {
40      pending.current.push(fn);
41      if (navigationRef.current) {
42        flush();
43      }
44    },
45    [flush]
46  );
47
48  return push;
49}
50
51export function useOptionalNavigation(): GenericNavigation | null {
52  const [navigation, setNavigation] = useState<GenericNavigation | null>(null);
53  const loadNavigation = useLoadedNavigation();
54
55  useEffect(() => {
56    loadNavigation((nav) => setNavigation(nav));
57  }, []);
58
59  return navigation;
60}
61