UNPKG

2.79 kBTypeScriptView Raw
1import type { ParamListBase } from '@react-navigation/routers';
2import * as React from 'react';
3
4import NavigationBuilderContext from './NavigationBuilderContext';
5import NavigationStateContext from './NavigationStateContext';
6import type { NavigationProp } from './types';
7
8type Options = {
9 key?: string;
10 navigation?: NavigationProp<ParamListBase>;
11 options?: object | undefined;
12};
13
14export default function useOptionsGetters({
15 key,
16 options,
17 navigation,
18}: Options) {
19 const optionsRef = React.useRef<object | undefined>(options);
20 const optionsGettersFromChildRef = React.useRef<
21 Record<string, () => object | undefined | null>
22 >({});
23
24 const { onOptionsChange } = React.useContext(NavigationBuilderContext);
25 const { addOptionsGetter: parentAddOptionsGetter } = React.useContext(
26 NavigationStateContext
27 );
28
29 const optionsChangeListener = React.useCallback(() => {
30 const isFocused = navigation?.isFocused() ?? true;
31 const hasChildren = Object.keys(optionsGettersFromChildRef.current).length;
32
33 if (isFocused && !hasChildren) {
34 onOptionsChange(optionsRef.current ?? {});
35 }
36 }, [navigation, onOptionsChange]);
37
38 React.useEffect(() => {
39 optionsRef.current = options;
40 optionsChangeListener();
41
42 return navigation?.addListener('focus', optionsChangeListener);
43 }, [navigation, options, optionsChangeListener]);
44
45 const getOptionsFromListener = React.useCallback(() => {
46 for (let key in optionsGettersFromChildRef.current) {
47 if (optionsGettersFromChildRef.current.hasOwnProperty(key)) {
48 const result = optionsGettersFromChildRef.current[key]?.();
49
50 // null means unfocused route
51 if (result !== null) {
52 return result;
53 }
54 }
55 }
56
57 return null;
58 }, []);
59
60 const getCurrentOptions = React.useCallback(() => {
61 const isFocused = navigation?.isFocused() ?? true;
62
63 if (!isFocused) {
64 return null;
65 }
66
67 const optionsFromListener = getOptionsFromListener();
68
69 if (optionsFromListener !== null) {
70 return optionsFromListener;
71 }
72
73 return optionsRef.current;
74 }, [navigation, getOptionsFromListener]);
75
76 React.useEffect(() => {
77 return parentAddOptionsGetter?.(key!, getCurrentOptions);
78 }, [getCurrentOptions, parentAddOptionsGetter, key]);
79
80 const addOptionsGetter = React.useCallback(
81 (key: string, getter: () => object | undefined | null) => {
82 optionsGettersFromChildRef.current[key] = getter;
83 optionsChangeListener();
84
85 return () => {
86 // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
87 delete optionsGettersFromChildRef.current[key];
88 optionsChangeListener();
89 };
90 },
91 [optionsChangeListener]
92 );
93
94 return {
95 addOptionsGetter,
96 getCurrentOptions,
97 };
98}