1 | import * as React from 'react';
|
2 |
|
3 | import useNavigation from './useNavigation';
|
4 |
|
5 | type EffectCallback = () => undefined | void | (() => void);
|
6 |
|
7 | /**
|
8 | * Hook to run an effect in a focused screen, similar to `React.useEffect`.
|
9 | * This can be used to perform side-effects such as fetching data or subscribing to events.
|
10 | * The passed callback should be wrapped in `React.useCallback` to avoid running the effect too often.
|
11 | *
|
12 | * @param callback Memoized callback containing the effect, should optionally return a cleanup function.
|
13 | */
|
14 | export default function useFocusEffect(effect: EffectCallback) {
|
15 | const navigation = useNavigation();
|
16 |
|
17 | if (arguments[1] !== undefined) {
|
18 | const message =
|
19 | "You passed a second argument to 'useFocusEffect', but it only accepts one argument. " +
|
20 | "If you want to pass a dependency array, you can use 'React.useCallback':\n\n" +
|
21 | 'useFocusEffect(\n' +
|
22 | ' React.useCallback(() => {\n' +
|
23 | '
|
24 | ' }, [depA, depB])\n' +
|
25 | ');\n\n' +
|
26 | 'See usage guide: https://reactnavigation.org/docs/use-focus-effect';
|
27 |
|
28 | console.error(message);
|
29 | }
|
30 |
|
31 | React.useEffect(() => {
|
32 | let isFocused = false;
|
33 | let cleanup: undefined | void | (() => void);
|
34 |
|
35 | const callback = () => {
|
36 | const destroy = effect();
|
37 |
|
38 | if (destroy === undefined || typeof destroy === 'function') {
|
39 | return destroy;
|
40 | }
|
41 |
|
42 | if (process.env.NODE_ENV !== 'production') {
|
43 | let message =
|
44 | 'An effect function must not return anything besides a function, which is used for clean-up.';
|
45 |
|
46 | if (destroy === null) {
|
47 | message +=
|
48 | " You returned 'null'. If your effect does not require clean-up, return 'undefined' (or nothing).";
|
49 | } else if (typeof (destroy as any).then === 'function') {
|
50 | message +=
|
51 | "\n\nIt looks like you wrote 'useFocusEffect(async () => ...)' or returned a Promise. " +
|
52 | 'Instead, write the async function inside your effect ' +
|
53 | 'and call it immediately:\n\n' +
|
54 | 'useFocusEffect(\n' +
|
55 | ' React.useCallback(() => {\n' +
|
56 | ' async function fetchData() {\n' +
|
57 | '
|
58 | ' const response = await MyAPI.getData(someId);\n' +
|
59 | '
|
60 | ' }\n\n' +
|
61 | ' fetchData();\n' +
|
62 | ' }, [someId])\n' +
|
63 | ');\n\n' +
|
64 | 'See usage guide: https:
|
65 | } else {
|
66 | message += ` You returned '${JSON.stringify(destroy)}'.`;
|
67 | }
|
68 |
|
69 | console.error(message);
|
70 | }
|
71 | };
|
72 |
|
73 |
|
74 | if (navigation.isFocused()) {
|
75 | cleanup = callback();
|
76 | isFocused = true;
|
77 | }
|
78 |
|
79 | const unsubscribeFocus = navigation.addListener('focus', () => {
|
80 |
|
81 |
|
82 | if (isFocused) {
|
83 | return;
|
84 | }
|
85 |
|
86 | if (cleanup !== undefined) {
|
87 | cleanup();
|
88 | }
|
89 |
|
90 | cleanup = callback();
|
91 | isFocused = true;
|
92 | });
|
93 |
|
94 | const unsubscribeBlur = navigation.addListener('blur', () => {
|
95 | if (cleanup !== undefined) {
|
96 | cleanup();
|
97 | }
|
98 |
|
99 | cleanup = undefined;
|
100 | isFocused = false;
|
101 | });
|
102 |
|
103 | return () => {
|
104 | if (cleanup !== undefined) {
|
105 | cleanup();
|
106 | }
|
107 |
|
108 | unsubscribeFocus();
|
109 | unsubscribeBlur();
|
110 | };
|
111 | }, [effect, navigation]);
|
112 | }
|
113 |
|
\ | No newline at end of file |