Skip to content
This repository has been archived by the owner on Dec 3, 2022. It is now read-only.

Commit

Permalink
Merge pull request #43 from react-navigation/feature/focus-based-hooks
Browse files Browse the repository at this point in the history
useFocusEffect / useIsFocused
  • Loading branch information
slorber committed Oct 2, 2019
2 parents 11aa5b1 + d354567 commit 2a5b198
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 3 deletions.
77 changes: 74 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
# React Navigation Hooks

[![npm version](https://badge.fury.io/js/react-navigation-hooks.svg)](https://badge.fury.io/js/react-navigation-hooks) [![npm downloads](https://img.shields.io/npm/dm/react-navigation-hooks.svg)](https://www.npmjs.com/package/react-navigation-hooks) [![CircleCI badge](https://circleci.com/gh/react-navigation/hooks/tree/master.svg?style=shield)](https://circleci.com/gh/react-navigation/hooks/tree/master) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://reactnavigation.org/docs/contributing.html)

🏄‍♀️ Surfing the wave of React Hook hype with a few convenience hooks for `@react-navigation/core` v3. Destined to work on web, server, and React Native. Contributions welcome!
🏄‍♀️ Surfing the wave of React Hook hype with a few convenience hooks for `@react-navigation/core` v3/v4. Destined to work on web, server, and React Native. Contributions welcome!

**IMPORTANT**: [react-navigation v5](https://github.com/react-navigation/navigation-ex) is already on its way and is a full rewrite (including hooks). This project will not live past v4, and will try to make the migration path from v4 to v5 easy by not introducing any new hook that won't be in v5.

## Examples (web only so far)

Expand Down Expand Up @@ -95,13 +98,80 @@ function ReportNavigationEvents() {

The event payload will be the same as provided by `addListener`, as documented here: https://reactnavigation.org/docs/en/navigation-prop.html#addlistener-subscribe-to-updates-to-navigation-lifecycle

### useIsFocused()

Convenient way to know if the screen currently has focus.

```js
function MyScreen() {
const isFocused = useIsFocused();
return <Text>{isFocused ? 'Focused' : 'Not Focused'}</Text>;
}
```

### useFocusEffect(callback)

Permit to execute an effect when the screen takes focus, and cleanup the effect when the screen loses focus.

```js
function MyScreen() {
useFocusEffect(useCallback(() => {
console.debug("screen takes focus");
return () => console.debug("screen loses focus");
}, []));
return <View>...</View>;
}
```

**NOTE**: To avoid the running the effect too often, it's important to wrap the callback in useCallback before passing it to `useFocusEffect` as shown in the example. The effect will re-execute everytime the callback changes if the screen is focused.

`useFocusEffect` can be helpful to refetch some screen data on params changes:

```js
function Profile({ userId }) {
const [user, setUser] = React.useState(null);

const fetchUser = React.useCallback(() => {
const request = API.fetchUser(userId).then(
data => setUser(data),
error => alert(error.message)
);

return () => request.abort();
}, [userId]);

useFocusEffect(fetchUser);

return <ProfileContent user={user} />;
}
```


`useFocusEffect` can be helpful to handle hardware back behavior on currently focused screen:

```js
const useBackHandler = (backHandler: () => boolean) => {
useFocusEffect(() => {
const subscription = BackHandler.addEventListener('hardwareBackPress', backHandler);
return () => subscription.remove();
});
};
```




### useFocusState()

**deprecated**: this hook does not exist in v5, you should rather use `useIsFocused`


Convenient way of subscribing to events and observing focus state of the current screen.

```js
function MyFocusTag() {
return <p>{useFocusState().isFocused ? 'Focused' : 'Not Focused'}</p>;
function MyScreen() {
const focusState = useFocusState();
return <Text>{focusState.isFocused ? 'Focused' : 'Not Focused'}</Text>;
}
```

Expand All @@ -111,3 +181,4 @@ One (always, and only one) of the following values will be true in the focus sta
- isBlurring
- isBlurred
- isFocusing

65 changes: 65 additions & 0 deletions src/Hooks.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
useState,
useContext,
useEffect,
useLayoutEffect,
useRef,
useCallback,
Expand Down Expand Up @@ -143,3 +144,67 @@ export function useFocusState() {

return focusState;
}

type EffectCallback = (() => void) | (() => () => void);

// Inspired by same hook from react-navigation v5
// See https://github.com/react-navigation/hooks/issues/39#issuecomment-534694135
export const useFocusEffect = (callback: EffectCallback) => {
const navigation = useNavigation();

useEffect(() => {
let isFocused = false;
let cleanup: (() => void) | void;

if (navigation.isFocused()) {
cleanup = callback();
isFocused = true;
}

const focusSubscription = navigation.addListener('willFocus', () => {
// If callback was already called for focus, avoid calling it again
// The focus event may also fire on intial render, so we guard against runing the effect twice
if (isFocused) {
return;
}

cleanup && cleanup();
cleanup = callback();
isFocused = true;
});

const blurSubscription = navigation.addListener('willBlur', () => {
cleanup && cleanup();
cleanup = undefined;
isFocused = false;
});

return () => {
cleanup && cleanup();
focusSubscription.remove();
blurSubscription.remove();
};
}, [callback, navigation]);
};

export const useIsFocused = () => {
const navigation = useNavigation();
const getNavigation = useGetter(navigation);
const [focused, setFocused] = useState(navigation.isFocused);

useEffect(() => {
const nav = getNavigation();
const focusSubscription = nav.addListener('willFocus', () =>
setFocused(true)
);
const blurSubscription = nav.addListener('willBlur', () =>
setFocused(false)
);
return () => {
focusSubscription.remove();
blurSubscription.remove();
};
}, [getNavigation]);

return focused;
};

0 comments on commit 2a5b198

Please sign in to comment.