Sometimes we want to run side-effects when a screen is focused. A side effect may involve things like adding an event listener, fetching data, updating document title, etc. While this can be achieved using
blur events, it's not very ergonomic.
To make this easier, the library exports a
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Note: To avoid the running the effect too often, it's important to wrap the callback in
useCallbackbefore passing it to
useFocusEffectas shown in the example.
useFocusEffect is analogous to React's
useEffect hook. The only difference is that it only runs if the screen is currently focused.
The effect will run whenever the dependencies passed to
React.useCallback change, i.e. it'll run on initial render (if the screen is focused) as well as on subsequent renders if the dependencies have changed. If you don't wrap your effect in
React.useCallback, the effect will run every render if the screen is focused.
The cleanup function runs when the previous effect needs to be cleaned up, i.e. when dependencies change and a new effect is scheduled and when the screen unmounts or blurs.
Running asynchronous effects¶
When running asynchronous effects such as fetching data from server, it's important to make sure that you cancel the request in the cleanup function (similar to
React.useEffect). If you're using an API that doesn't provide a cancellation mechanism, make sure to ignore the state updates:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
If you don't ignore the result, then you might end up with inconsistent data due to race conditions in your API calls.
Delaying effect until transition finishes¶
useFocusEffect hook runs the effect as soon as the screen comes into focus. This often means that if there is an animation for the screen change, it might not have finished yet.
React Navigation runs its animations in native thread, so it's not a problem in many cases. But if the effect updates the UI or renders something expensive, then it can affect the animation performance. In such cases, we can use
InteractionManager to defer our work until the animations or gestures have finished:
1 2 3 4 5 6 7 8 9
useFocusEffect different from adding a listener for
focus event fires when a screen comes into focus. Since it's an event, your listener won't be called if the screen was already focused when you subscribed to the event. This also doesn't provide a way to perform a cleanup function when the screen becomes unfocused. You can subscribe to the
blur event and handle it manually, but it can get messy. You will usually need to handle
componentWillUnmount as well in addition to these events, which complicates it even more.
useFocusEffect allows you to run an effect on focus and clean it up when the screen becomes unfocused. It also handles cleanup on unmount. It re-runs the effect when dependencies change, so you don't need to worry about stale values in your listener.
When to use
blur events instead¶
useEffect, a cleanup function can be returned from the effect in
useFocusEffect. The cleanup function is intended to cleanup the effect - e.g. abort an asynchronous task, unsubscribe from an event listener, etc. It's not intended to be used to do something on
For example, don't do the following:
1 2 3 4 5 6 7
The cleanup function runs whenever the effect needs to cleanup, i.e. on
blur, unmount, dependency change etc. It's not a good place to update the state or do something that should happen on
blur. You should use listen to the
blur event instead:
1 2 3 4 5 6 7
Similarly, if you want to do something when the screen receives focus (e.g. track screen focus) and it doesn't need cleanup or need to be re-run on dependency changes, then you should use the
focus event instead:
Using with class component¶
You can make a component for your effect and use it in your class component:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31