Skip to content

Commit

Permalink
Migrate 'Navigation events' examples to static API in v7 (#1330)
Browse files Browse the repository at this point in the history
* Add static examples

* Remvoe dependencies, default and use useNavigation instead of prop

* Navigation events cleanup

---------

Co-authored-by: kacperkapusciak <[email protected]>
  • Loading branch information
patrycjakalinska and kacperkapusciak authored Mar 5, 2024
1 parent d6a0529 commit 1832930
Show file tree
Hide file tree
Showing 2 changed files with 239 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,10 @@ function Settings({ route }) {
}

const RootStack = createNativeStackNavigator({
Home: Home,
Settings: Settings,
screens: {
Home: Home,
Settings: Settings,
},
});

const Navigation = createStaticNavigation(RootStack);
Expand Down
240 changes: 235 additions & 5 deletions versioned_docs/version-7.x/navigation-events.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ title: Navigation events
sidebar_label: Navigation events
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

You can listen to various events emitted by React Navigation to get notified of certain events, and in some cases, override the default action. There are few core events such as `focus`, `blur` etc. (documented below) that work for every navigator, as well as navigator specific events that work only for certain navigators.

Apart from the core events, each navigator can emit their own custom events. For example, stack navigator emits `transitionStart` and `transitionEnd` events, tab navigator emits `tabPress` event etc. You can find details about the events emitted on the individual navigator's documentation.
Expand Down Expand Up @@ -55,24 +58,146 @@ const unsubscribe = navigation.addListener('tabPress', (e) => {

Normally, you'd add an event listener in `React.useEffect` for function components. For example:

<samp id="simple-focus-and-blur" />
<Tabs groupId="config" queryString="config">
<TabItem value="static" label="Static" default>

```js
function Profile() {
```js name="navigation.addListener with focus" snack version=7
import * as React from 'react';
import { View, Text } from 'react-native';
import { Button } from '@react-navigation/elements';
import {
createStaticNavigation,
useNavigation,
} from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';

function SettingsScreen() {
const navigation = useNavigation();

return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Settings Screen</Text>
<Button onPress={() => navigation.navigate('Profile')}>
Go to Profile
</Button>
</View>
);
}

// codeblock-focus-start
function ProfileScreen() {
const navigation = useNavigation();

React.useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
// do something
// Screen was focused
});
return unsubscribe;
}, [navigation]);

React.useEffect(() => {
const unsubscribe = navigation.addListener('blur', () => {
// Screen was unfocused
});
return unsubscribe;
}, [navigation]);

return <ProfileContent />;
// Rest of the component
// codeblock-focus-end
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Profile Screen</Text>
<Button onPress={() => navigation.navigate('Settings')}>
Go to Settings
</Button>
</View>
);
// codeblock-focus-start
}
// codeblock-focus-end

const SettingsStack = createNativeStackNavigator({
screens: {
Settings: SettingsScreen,
Profile: ProfileScreen,
},
});

const Navigation = createStaticNavigation(SettingsStack);

export default function App() {
return <Navigation />;
}
```

</TabItem>
<TabItem value="dynamic" label="Dynamic">

```js name="navigation.addListener with focus" snack version=7
import * as React from 'react';
import { View, Text } from 'react-native';
import { Button } from '@react-navigation/elements';
import { NavigationContainer, useNavigation } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';

function SettingsScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Settings Screen</Text>
<Button onPress={() => navigation.navigate('Profile')}>
Go to Profile
</Button>
</View>
);
}

// codeblock-focus-start
function ProfileScreen({ navigation }) {
React.useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
// Screen was focused
});
return unsubscribe;
}, [navigation]);

React.useEffect(() => {
const unsubscribe = navigation.addListener('blur', () => {
// Screen was unfocused
});
return unsubscribe;
}, [navigation]);

// Rest of the component
// codeblock-focus-end
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Profile Screen</Text>
<Button onPress={() => navigation.navigate('Settings')}>
Go to Settings
</Button>
</View>
);
// codeblock-focus-start
}
// codeblock-focus-end

const SettingsStack = createNativeStackNavigator();

export default function App() {
return (
<NavigationContainer>
<SettingsStack.Navigator>
<SettingsStack.Screen name="Settings" component={SettingsScreen} />
<SettingsStack.Screen name="Profile" component={ProfileScreen} />
</SettingsStack.Navigator>
</NavigationContainer>
);
}
```

</TabItem>
</Tabs>

The `unsubscribe` function can be returned as the cleanup function in the effect.

For class components, you can add the event in the `componentDidMount` lifecycle method and unsubscribe in `componentWillUnmount`:
Expand Down Expand Up @@ -113,6 +238,28 @@ Sometimes you might want to add a listener from the component where you defined

Example:

<Tabs groupId="config" queryString="config">
<TabItem value="static" label="Static" default>

```js
const Tab = createBottomTabNavigatior({
screens: {
Chat: {
screen: Chat,
listeners: {
tabPress: (e) => {
// Prevent default action
e.preventDefault;
},
},
},
},
});
```

</TabItem>
<TabItem value="dynamic" label="Dynamic">

```js
<Tab.Screen
name="Chat"
Expand All @@ -126,9 +273,36 @@ Example:
/>
```

</TabItem>
</Tabs>

You can also pass a callback which returns the object with listeners. It'll receive `navigation` and `route` as the arguments.

Example:
<Tabs groupId="config" queryString="config">
<TabItem value="static" label="Static" default>

```js
const Tab = createBottomTabNavigatior({
screens: {
Chat: {
screen: Chat,
listeners: ({ navigation, route }) => ({
tabPress: (e) => {
// Prevent default action
e.preventDefault;

// Do something with the `navigation` object
navigation.navigate('AnotherPlace');
},
}),
},
},
});
```

</TabItem>
<TabItem value="dynamic" label="Dynamic">

```js
<Tab.Screen
Expand All @@ -146,12 +320,36 @@ Example:
/>
```

</TabItem>
</Tabs>

### `screenListeners` prop on the navigator

You can pass a prop named `screenListeners` to the navigator component, where you can specify listeners for events from all screens for this navigator. This can be useful if you want to listen to specific events regardless of the screen, or want to listen to common events such as `state` which is emitted to all screens.

Example:

<Tabs groupId="config" queryString="config">
<TabItem value="static" label="Static" default>

```js
const Stack = createNativeStackNavigator({
screenListeners: {
state: (e) => {
// Do something with the state
console.log('state changed', e.data);
},
},
screens: {
Home: HomeScreen,
Profile: ProfileScreen,
},
});
```

</TabItem>
<TabItem value="dynamic" label="Dynamic">

```js
<Stack.Navigator
screenListeners={{
Expand All @@ -166,8 +364,37 @@ Example:
</Stack.Navigator>
```

</TabItem>
</Tabs>

Similar to `listeners`, you can also pass a function to `screenListeners`. The function will receive the [`navigation` object](navigation-object.md) and the [`route` object](route-object.md) for each screen. This can be useful if you need access to the `navigation` object.

<Tabs groupId="config" queryString="config">
<TabItem value="static" label="Static" default>

```js
const Tab = createBottomTabNavigatior({
screenListeners: ({ navigation }) => ({
state: (e) => {
// Do something with the state
console.log('state changed', e.data);

// Do something with the `navigation` object
if (!navigation.canGoBack()) {
console.log("we're on the initial screen");
}
},
}),
screens: {
Home: HomeScreen,
Profile: ProfileScreen,
},
});
```

</TabItem>
<TabItem value="dynamic" label="Dynamic">

```js
<Tab.Navigator
screenListeners={({ navigation }) => ({
Expand All @@ -186,3 +413,6 @@ Similar to `listeners`, you can also pass a function to `screenListeners`. The f
<Tab.Screen name="Profile" component={ProfileScreen} />
</Tab.Navigator>
```

</TabItem>
</Tabs>

0 comments on commit 1832930

Please sign in to comment.