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