Skip to content

Commit 5fd31d4

Browse files
Migrate examples to static API in v7 (#1302)
1 parent f41ab91 commit 5fd31d4

16 files changed

+4729
-200
lines changed

versioned_docs/version-7.x/auth-flow.md

Lines changed: 845 additions & 55 deletions
Large diffs are not rendered by default.

versioned_docs/version-7.x/custom-android-back-button-handling.md

Lines changed: 225 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ title: Custom Android back button behavior
44
sidebar_label: Custom Android back button behavior
55
---
66

7+
import Tabs from '@theme/Tabs';
8+
import TabItem from '@theme/TabItem';
9+
710
By default, when user presses the Android hardware back button, react-navigation will pop a screen or exit the app if there are no screens to pop. This is a sensible default behavior, but there are situations when you might want to implement custom handling.
811

912
As an example, consider a screen where user is selecting items in a list, and a "selection mode" is active. On a back button press, you would first want the "selection mode" to be deactivated, and the screen should be popped only on the second back button press. The following code snippet demonstrates the situation. We make use of [`BackHandler`](https://reactnative.dev/docs/backhandler.html) which comes with react-native, along with the `useFocusEffect` hook to add our custom `hardwareBackPress` listener.
@@ -12,31 +15,248 @@ Returning `true` from `onBackPress` denotes that we have handled the event, and
1215

1316
<samp id="custom-android-back-button"/>
1417

15-
```js
18+
<Tabs groupId="config" queryString="config">
19+
<TabItem value="static" label="Static" default>
20+
21+
```js name="Custom android back button" snack version=7
22+
import * as React from 'react';
23+
import {
24+
Pressable,
25+
Text,
26+
View,
27+
Button,
28+
BackHandler,
29+
StyleSheet,
30+
} from 'react-native';
31+
import { createStaticNavigation } from '@react-navigation/native';
32+
import { useFocusEffect } from '@react-navigation/native';
33+
import { createNativeStackNavigator } from '@react-navigation/native-stack';
34+
35+
const listData = [{ key: 'Apple' }, { key: 'Orange' }, { key: 'Carrot' }];
36+
37+
// codeblock-focus-start
38+
function ScreenWithCustomBackBehavior() {
39+
// codeblock-focus-end
40+
const [selected, setSelected] = React.useState(listData[0].key);
41+
const [isSelectionModeEnabled, setIsSelectionModeEnabled] =
42+
React.useState(false);
43+
44+
// codeblock-focus-start
45+
// ...
46+
47+
useFocusEffect(
48+
React.useCallback(() => {
49+
const onBackPress = () => {
50+
if (isSelectionModeEnabled) {
51+
setIsSelectionModeEnabled(false);
52+
return true;
53+
} else {
54+
return false;
55+
}
56+
};
57+
58+
const subscription = BackHandler.addEventListener(
59+
'hardwareBackPress',
60+
onBackPress
61+
);
62+
63+
return () => subscription.remove();
64+
}, [isSelectionModeEnabled])
65+
);
66+
// codeblock-focus-end
67+
68+
return (
69+
<View style={styles.container}>
70+
{listData.map((item) => (
71+
<>
72+
{isSelectionModeEnabled ? (
73+
<Pressable
74+
onPress={() => {
75+
setSelected(item.key);
76+
}}
77+
style={{
78+
textDecorationLine: item.key === selected ? 'underline' : '',
79+
}}
80+
>
81+
<Text
82+
style={{
83+
textDecorationLine: item.key === selected ? 'underline' : '',
84+
...styles.text,
85+
}}
86+
>
87+
{item.key}
88+
</Text>
89+
</Pressable>
90+
) : (
91+
<Text style={styles.text}>
92+
{item.key === selected ? 'Selected: ' : ''}
93+
{item.key}
94+
</Text>
95+
)}
96+
</>
97+
))}
98+
<Button
99+
title="Toggle selection mode"
100+
onPress={() => setIsSelectionModeEnabled(!isSelectionModeEnabled)}
101+
/>
102+
<Text>Selection mode: {isSelectionModeEnabled ? 'ON' : 'OFF'}</Text>
103+
</View>
104+
);
105+
// codeblock-focus-start
106+
107+
// ...
108+
}
109+
// codeblock-focus-end
110+
111+
const RootStack = createNativeStackNavigator({
112+
screens: {
113+
CustomScreen: ScreenWithCustomBackBehavior,
114+
},
115+
});
116+
117+
const Navigation = createStaticNavigation(RootStack);
118+
119+
export default function App() {
120+
return <Navigation />;
121+
}
122+
123+
const styles = StyleSheet.create({
124+
container: {
125+
flex: 1,
126+
alignItems: 'center',
127+
justifyContent: 'center',
128+
},
129+
text: {
130+
fontSize: 20,
131+
marginBottom: 20,
132+
},
133+
});
134+
```
135+
136+
</TabItem>
137+
<TabItem value="dynamic" label="Dynamic" default>
138+
139+
```js name="Custom android back button" snack version=7
140+
import * as React from 'react';
141+
import {
142+
Pressable,
143+
Text,
144+
View,
145+
Button,
146+
BackHandler,
147+
StyleSheet,
148+
} from 'react-native';
149+
import { NavigationContainer } from '@react-navigation/native';
150+
import { useFocusEffect } from '@react-navigation/native';
151+
import { createNativeStackNavigator } from '@react-navigation/native-stack';
152+
153+
const Stack = createNativeStackNavigator();
154+
155+
const listData = [{ key: 'Apple' }, { key: 'Orange' }, { key: 'Carrot' }];
156+
157+
// codeblock-focus-start
16158
function ScreenWithCustomBackBehavior() {
159+
// codeblock-focus-end
160+
161+
const [selected, setSelected] = React.useState(listData[0].key);
162+
const [isSelectionModeEnabled, setIsSelectionModeEnabled] =
163+
React.useState(false);
164+
// codeblock-focus-start
17165
// ...
18166

19167
useFocusEffect(
20168
React.useCallback(() => {
21169
const onBackPress = () => {
22-
if (isSelectionModeEnabled()) {
23-
disableSelectionMode();
170+
if (isSelectionModeEnabled) {
171+
setIsSelectionModeEnabled(false);
24172
return true;
25173
} else {
26174
return false;
27175
}
28176
};
29177

30-
const subscription = BackHandler.addEventListener('hardwareBackPress', onBackPress);
178+
const subscription = BackHandler.addEventListener(
179+
'hardwareBackPress',
180+
onBackPress
181+
);
31182

32183
return () => subscription.remove();
33-
}, [isSelectionModeEnabled, disableSelectionMode])
184+
}, [isSelectionModeEnabled])
185+
);
186+
// codeblock-focus-end
187+
188+
return (
189+
<View style={styles.container}>
190+
{listData.map((item) => (
191+
<>
192+
{isSelectionModeEnabled ? (
193+
<Pressable
194+
onPress={() => {
195+
setSelected(item.key);
196+
}}
197+
style={{
198+
textDecorationLine: item.key === selected ? 'underline' : '',
199+
}}
200+
>
201+
<Text
202+
style={{
203+
textDecorationLine: item.key === selected ? 'underline' : '',
204+
...styles.text,
205+
}}
206+
>
207+
{item.key}
208+
</Text>
209+
</Pressable>
210+
) : (
211+
<Text style={styles.text}>
212+
{item.key === selected ? 'Selected: ' : ''}
213+
{item.key}
214+
</Text>
215+
)}
216+
</>
217+
))}
218+
<Button
219+
title="Toggle selection mode"
220+
onPress={() => setIsSelectionModeEnabled(!isSelectionModeEnabled)}
221+
/>
222+
<Text>Selection mode: {isSelectionModeEnabled ? 'ON' : 'OFF'}</Text>
223+
</View>
34224
);
225+
// codeblock-focus-start
35226

36227
// ...
37228
}
229+
// codeblock-focus-end
230+
231+
export default function App() {
232+
return (
233+
<NavigationContainer>
234+
<Stack.Navigator>
235+
<Stack.Screen
236+
name="CustomScreen"
237+
component={ScreenWithCustomBackBehavior}
238+
/>
239+
</Stack.Navigator>
240+
</NavigationContainer>
241+
);
242+
}
243+
244+
const styles = StyleSheet.create({
245+
container: {
246+
flex: 1,
247+
alignItems: 'center',
248+
justifyContent: 'center',
249+
},
250+
text: {
251+
fontSize: 20,
252+
marginBottom: 20,
253+
},
254+
});
38255
```
39256

257+
</TabItem>
258+
</Tabs>
259+
40260
The presented approach will work well for screens that are shown in a `StackNavigator`. Custom back button handling in other situations may not be supported at the moment (eg. A known case when this does not work is when you want to handle back button press in an open drawer. PRs for such use cases are welcome!).
41261

42262
If instead of overriding system back button, you'd like to prevent going back from the screen, see docs for [preventing going back](preventing-going-back.md).

versioned_docs/version-7.x/deep-linking.md

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ title: Deep linking
44
sidebar_label: Deep linking
55
---
66

7+
import Tabs from '@theme/Tabs';
8+
import TabItem from '@theme/TabItem';
9+
710
This guide will describe how to configure your app to handle deep links on various platforms. To handle incoming links, you need to handle 2 scenarios:
811

912
1. If the app wasn't previously open, the deep link needs to set the initial state
@@ -35,6 +38,28 @@ npx expo install expo-linking
3538

3639
Then, let's configure React Navigation to use the `scheme` for parsing incoming deep links:
3740

41+
<Tabs groupId="config" queryString="config">
42+
<TabItem value="static" label="Static" default>
43+
44+
```js
45+
import * as Linking from 'expo-linking';
46+
47+
const prefix = Linking.createURL('/');
48+
49+
/* content */
50+
51+
function App() {
52+
const linking = {
53+
prefixes: [prefix],
54+
};
55+
56+
return <Navigation linking={linking} />;
57+
}
58+
```
59+
60+
</TabItem>
61+
<TabItem value="dynamic" label="Dynamic" default>
62+
3863
```js
3964
import * as Linking from 'expo-linking';
4065

@@ -53,6 +78,9 @@ function App() {
5378
}
5479
```
5580

81+
</TabItem>
82+
</Tabs>
83+
5684
The reason that is necessary to use `Linking.createURL` is that the scheme will differ depending on whether you're in the client app or in a standalone app.
5785

5886
The scheme specified in `app.json` only applies to standalone apps. In the Expo client app you can deep link using `exp://ADDRESS:PORT/--/` where `ADDRESS` is often `127.0.0.1` and `PORT` is often `19000` - the URL is printed when you run `expo start`. The `Linking.createURL` function abstracts it out so that you don't need to specify them manually.
@@ -267,7 +295,59 @@ React Native's `Linking` isn't the only way to handle deep linking. You might al
267295

268296
To achieve this, you'd need to override how React Navigation subscribes to incoming links. To do so, you can provide your own [`getInitialURL`](navigation-container.md#linkinggetinitialurl) and [`subscribe`](navigation-container.md#linkingsubscribe) functions:
269297

270-
```js
298+
<Tabs groupId="config" queryString="config">
299+
<TabItem value="static" label="Static" default>
300+
301+
```js name="Third-party integrations"
302+
const linking = {
303+
prefixes: ['myapp://', 'https://myapp.com'],
304+
305+
// Custom function to get the URL which was used to open the app
306+
async getInitialURL() {
307+
// First, you would need to get the initial URL from your third-party integration
308+
// The exact usage depend on the third-party SDK you use
309+
// For example, to get the initial URL for Firebase Dynamic Links:
310+
const { isAvailable } = utils().playServicesAvailability;
311+
312+
if (isAvailable) {
313+
const initialLink = await dynamicLinks().getInitialLink();
314+
315+
if (initialLink) {
316+
return initialLink.url;
317+
}
318+
}
319+
320+
// As a fallback, you may want to do the default deep link handling
321+
const url = await Linking.getInitialURL();
322+
323+
return url;
324+
},
325+
326+
// Custom function to subscribe to incoming links
327+
subscribe(listener) {
328+
// Listen to incoming links from Firebase Dynamic Links
329+
const unsubscribeFirebase = dynamicLinks().onLink(({ url }) => {
330+
listener(url);
331+
});
332+
333+
// Listen to incoming links from deep linking
334+
const linkingSubscription = Linking.addEventListener('url', ({ url }) => {
335+
listener(url);
336+
});
337+
338+
return () => {
339+
// Clean up the event listeners
340+
unsubscribeFirebase();
341+
linkingSubscription.remove();
342+
};
343+
},
344+
};
345+
```
346+
347+
</TabItem>
348+
<TabItem value="dynamic" label="Dynamic">
349+
350+
```js name="Third-party integrations"
271351
const linking = {
272352
prefixes: ['myapp://', 'https://myapp.com'],
273353

@@ -317,4 +397,7 @@ const linking = {
317397
};
318398
```
319399

400+
</TabItem>
401+
</Tabs>
402+
320403
Similar to the above example, you can integrate any API that provides a way to get the initial URL and to subscribe to new incoming URLs using the `getInitialURL` and `subscribe` options.

0 commit comments

Comments
 (0)