diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 2618dc1431..123190557c 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,3 +1,5 @@ + diff --git a/blog/2020-05-16-web-support.md b/blog/2020-05-16-web-support.md index 599bf8357e..0f5be8529c 100644 --- a/blog/2020-05-16-web-support.md +++ b/blog/2020-05-16-web-support.md @@ -34,7 +34,7 @@ Example: ```js const linking = { - prefixes: ['https://mychat.com/', 'mychat://'], + prefixes: ['https://example.com/', 'example://'], config: { screens: { Home: '', diff --git a/blog/2024-03-25-introducing-static-api.md b/blog/2024-03-25-introducing-static-api.md index fc950b677b..014d7567e8 100644 --- a/blog/2024-03-25-introducing-static-api.md +++ b/blog/2024-03-25-introducing-static-api.md @@ -138,7 +138,7 @@ There are 2 improvements to deep linking API: return ( + ## Introduction React Navigation comes with many navigators out of the box. We've got Stack, Native Stack, Drawer, and Bottom Tabs, but there were no Native Bottom Tabs until today! diff --git a/versioned_docs/version-1.x/deep-linking.md b/versioned_docs/version-1.x/deep-linking.md index 4e3b7e229a..bbbffeb5ba 100644 --- a/versioned_docs/version-1.x/deep-linking.md +++ b/versioned_docs/version-1.x/deep-linking.md @@ -4,7 +4,7 @@ title: Deep linking sidebar_label: Deep linking --- -In this guide we will set up our app to handle external URIs. Let's suppose that we want a URI like `mychat://chat/Eric` to open our app and link straight into a chat screen for some user named "Eric". +In this guide we will set up our app to handle external URIs. Let's suppose that we want a URI like `example://chat/Eric` to open our app and link straight into a chat screen for some user named "Eric". ## Configuration @@ -36,7 +36,7 @@ You need to specify a scheme for your app. You can register for a scheme in your ```json { "expo": { - "scheme": "mychat" + "scheme": "example" } } ``` @@ -90,14 +90,14 @@ Next, let's configure our navigation container to extract the path from the app' ```js const SimpleApp = StackNavigator({...})); -const prefix = 'mychat://'; +const prefix = 'example://'; const MainApp = () => ; ``` ### iOS -Let's configure the native iOS app to open based on the `mychat://` URI scheme. +Let's configure the native iOS app to open based on the `example://` URI scheme. In `SimpleApp/ios/SimpleApp/AppDelegate.m`: @@ -116,7 +116,7 @@ In `SimpleApp/ios/SimpleApp/AppDelegate.m`: In Xcode, open the project at `SimpleApp/ios/SimpleApp.xcodeproj`. Select the project in sidebar and navigate to the info tab. Scroll down to "URL Types" and add one. In the new URL type, set the identifier and the URL scheme to your desired URL scheme. -![Xcode project info URL types with mychat added](/assets/deep-linking/xcode-linking.png) +![Xcode project info URL types with example added](/assets/deep-linking/xcode-linking.png) Now you can press play in Xcode, or re-build on the command line: @@ -127,10 +127,10 @@ react-native run-ios To test the URI on the simulator, run the following: ``` -xcrun simctl openurl booted mychat://chat/Eric +xcrun simctl openurl booted example://chat/Eric ``` -To test the URI on a real device, open Safari and type `mychat://chat/Eric`. +To test the URI on a real device, open Safari and type `example://chat/Eric`. ### Android @@ -153,7 +153,7 @@ In `SimpleApp/android/app/src/main/AndroidManifest.xml`, do these followings adj - + ``` @@ -167,7 +167,7 @@ react-native run-android To test the intent handling in Android, run the following: ``` -adb shell am start -W -a android.intent.action.VIEW -d "mychat://chat/Eric" com.simpleapp +adb shell am start -W -a android.intent.action.VIEW -d "example://chat/Eric" com.simpleapp ``` ## Disable deep linking diff --git a/versioned_docs/version-2.x/deep-linking.md b/versioned_docs/version-2.x/deep-linking.md index e0e0f6f385..1659d84a7a 100644 --- a/versioned_docs/version-2.x/deep-linking.md +++ b/versioned_docs/version-2.x/deep-linking.md @@ -4,7 +4,7 @@ title: Deep linking sidebar_label: Deep linking --- -In this guide we will set up our app to handle external URIs. Let's suppose that we want a URI like `mychat://chat/Eric` to open our app and link straight into a chat screen for some user named "Eric". +In this guide we will set up our app to handle external URIs. Let's suppose that we want a URI like `example://chat/Eric` to open our app and link straight into a chat screen for some user named "Eric". ## Configuration @@ -36,7 +36,7 @@ You need to specify a scheme for your app. You can register for a scheme in your ```json { "expo": { - "scheme": "mychat" + "scheme": "example" } } ``` @@ -90,14 +90,14 @@ Next, let's configure our navigation container to extract the path from the app' ```js const SimpleApp = createStackNavigator({...}); -const prefix = 'mychat://'; +const prefix = 'example://'; const MainApp = () => ; ``` ### iOS -Let's configure the native iOS app to open based on the `mychat://` URI scheme. +Let's configure the native iOS app to open based on the `example://` URI scheme. In `SimpleApp/ios/SimpleApp/AppDelegate.m`: @@ -116,7 +116,7 @@ In `SimpleApp/ios/SimpleApp/AppDelegate.m`: In Xcode, open the project at `SimpleApp/ios/SimpleApp.xcodeproj`. Select the project in sidebar and navigate to the info tab. Scroll down to "URL Types" and add one. In the new URL type, set the identifier and the URL scheme to your desired URL scheme. -![Xcode project info URL types with mychat added](/assets/deep-linking/xcode-linking.png) +![Xcode project info URL types with example added](/assets/deep-linking/xcode-linking.png) Now you can press play in Xcode, or re-build on the command line: @@ -127,10 +127,10 @@ react-native run-ios To test the URI on the simulator, run the following: ``` -xcrun simctl openurl booted mychat://chat/Eric +xcrun simctl openurl booted example://chat/Eric ``` -To test the URI on a real device, open Safari and type `mychat://chat/Eric`. +To test the URI on a real device, open Safari and type `example://chat/Eric`. ### Android @@ -153,7 +153,7 @@ In `SimpleApp/android/app/src/main/AndroidManifest.xml`, do these followings adj - + ``` @@ -167,7 +167,7 @@ react-native run-android To test the intent handling in Android, run the following: ``` -adb shell am start -W -a android.intent.action.VIEW -d "mychat://chat/Eric" com.simpleapp +adb shell am start -W -a android.intent.action.VIEW -d "example://chat/Eric" com.simpleapp ``` ## Disable deep linking diff --git a/versioned_docs/version-3.x/deep-linking.md b/versioned_docs/version-3.x/deep-linking.md index 7adc9312c4..cce7c6f89b 100644 --- a/versioned_docs/version-3.x/deep-linking.md +++ b/versioned_docs/version-3.x/deep-linking.md @@ -4,7 +4,7 @@ title: Deep linking sidebar_label: Deep linking --- -In this guide we will set up our app to handle external URIs. Let's suppose that we want a URI like `mychat://chat/Eric` to open our app and link straight into a chat screen for some user named "Eric". +In this guide we will set up our app to handle external URIs. Let's suppose that we want a URI like `example://chat/Eric` to open our app and link straight into a chat screen for some user named "Eric". ## Configuration @@ -71,7 +71,7 @@ You need to specify a scheme for your app. You can register for a scheme in your ```json { "expo": { - "scheme": "mychat" + "scheme": "example" } } ``` @@ -125,14 +125,14 @@ Next, let's configure our navigation container to extract the path from the app' ```js const SimpleApp = createAppContainer(createStackNavigator({...})); -const prefix = 'mychat://'; +const prefix = 'example://'; const MainApp = () => ; ``` ### iOS -Let's configure the native iOS app to open based on the `mychat://` URI scheme. +Let's configure the native iOS app to open based on the `example://` URI scheme. In `SimpleApp/ios/SimpleApp/AppDelegate.m`: @@ -151,7 +151,7 @@ In `SimpleApp/ios/SimpleApp/AppDelegate.m`: In Xcode, open the project at `SimpleApp/ios/SimpleApp.xcodeproj`. Select the project in sidebar and navigate to the info tab. Scroll down to "URL Types" and add one. In the new URL type, set the identifier and the URL scheme to your desired URL scheme. -![Xcode project info URL types with mychat added](/assets/deep-linking/xcode-linking.png) +![Xcode project info URL types with example added](/assets/deep-linking/xcode-linking.png) Now you can press play in Xcode, or re-build on the command line: @@ -162,10 +162,10 @@ react-native run-ios To test the URI on the simulator, run the following: ``` -xcrun simctl openurl booted mychat://chat/Eric +xcrun simctl openurl booted example://chat/Eric ``` -To test the URI on a real device, open Safari and type `mychat://chat/Eric`. +To test the URI on a real device, open Safari and type `example://chat/Eric`. ### Android @@ -188,7 +188,7 @@ In `SimpleApp/android/app/src/main/AndroidManifest.xml`, do these followings adj - + ``` @@ -202,7 +202,7 @@ react-native run-android To test the intent handling in Android, run the following: ``` -adb shell am start -W -a android.intent.action.VIEW -d "mychat://chat/Eric" com.simpleapp +adb shell am start -W -a android.intent.action.VIEW -d "example://chat/Eric" com.simpleapp ``` ## Disable deep linking diff --git a/versioned_docs/version-4.x/contributing.md b/versioned_docs/version-4.x/contributing.md index faf347fc79..5472a1c0ac 100644 --- a/versioned_docs/version-4.x/contributing.md +++ b/versioned_docs/version-4.x/contributing.md @@ -32,7 +32,7 @@ And here are a few helpful resources to aid in getting started: You can't write code without writing the occasional bug. Especially as React Navigation is moving quickly, bugs happen. When you think you've found one here's what to do: 1. Search the existing issues for one like what you're seeing. If you see one, add a 👍 reaction (please no +1 comments). Read through the comments and see if you can provide any more valuable information to the thread -2. If there are no other issues like yours then create a new one. Be sure to follow the [issue template](https://github.com/react-navigation/react-navigation/blob/4.x/.github/ISSUE_TEMPLATE.md). +2. If there are no other issues like yours then create a new one. Be sure to follow the [issue template](https://github.com/react-navigation/react-navigation/blob/v4.1.1/.github/ISSUE_TEMPLATE/bug_report.md). Creating a high quality reproduction is critical. Without it we likely can't fix the bug and, in an ideal situation, you'll find out that it's not actually a bug of the library but simply done incorrectly in your project. Instant bug fix! @@ -93,7 +93,7 @@ The libdef (located at `flow/react-navigation.js`) will need to be updated such ### Issue Template -Before submitting an issue, please take a look at the [issue template](https://github.com/react-navigation/react-navigation/blob/4.x/.github/ISSUE_TEMPLATE.md) and follow it. This is in place to help everyone better understand the issue you're having and reduce the back and forth to get the necessary information. +Before submitting an issue, please take a look at the [issue template](https://github.com/react-navigation/react-navigation/blob/v4.1.1/.github/ISSUE_TEMPLATE/bug_report.md) and follow it. This is in place to help everyone better understand the issue you're having and reduce the back and forth to get the necessary information. Yes, it takes time and effort to complete the issue template. But that's the only way to ask high quality questions that actually get responses. @@ -101,7 +101,7 @@ Would you rather take 1 minute to create an incomplete issue report and wait mon ### Pull Request Template -Much like the issue template, the [pull request template](https://github.com/react-navigation/react-navigation/blob/4.x/.github/PULL_REQUEST_TEMPLATE.md) lays out instructions to ensure your pull request gets reviewed in a timely manner and reduces the back and forth. Make sure to look it over before you start writing any code. +Much like the issue template, the [pull request template](https://github.com/react-navigation/react-navigation/blob/v4.1.1/.github/PULL_REQUEST_TEMPLATE.md) lays out instructions to ensure your pull request gets reviewed in a timely manner and reduces the back and forth. Make sure to look it over before you start writing any code. ### Forking the Repository diff --git a/versioned_docs/version-4.x/deep-linking.md b/versioned_docs/version-4.x/deep-linking.md index 60536603b4..4576a34ca3 100644 --- a/versioned_docs/version-4.x/deep-linking.md +++ b/versioned_docs/version-4.x/deep-linking.md @@ -4,7 +4,7 @@ title: Deep linking sidebar_label: Deep linking --- -In this guide we will set up our app to handle external URIs. Let's suppose that we want a URI like `mychat://chat/Eric` to open our app and link straight into a chat screen for some user named "Eric". +In this guide we will set up our app to handle external URIs. Let's suppose that we want a URI like `example://chat/Eric` to open our app and link straight into a chat screen for some user named "Eric". ## Configuration @@ -66,12 +66,12 @@ const FriendsScreen = createStackNavigator({ ## Set up with Expo projects -First, you will want to specify a URL scheme for your app. This corresponds to the string before `://` in a URL, so if your scheme is `mychat` then a link to your app would be `mychat://`. The scheme only applies to standalone apps and you need to re-build the standalone app for the change to take effect. 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`. If you want to test with your custom scheme you will need to run `expo build:ios -t simulator` or `expo build:android` and install the resulting binaries in your emulators. You can register for a scheme in your `app.json` by adding a string under the scheme key: +First, you will want to specify a URL scheme for your app. This corresponds to the string before `://` in a URL, so if your scheme is `example` then a link to your app would be `example://`. The scheme only applies to standalone apps and you need to re-build the standalone app for the change to take effect. 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`. If you want to test with your custom scheme you will need to run `expo build:ios -t simulator` or `expo build:android` and install the resulting binaries in your emulators. You can register for a scheme in your `app.json` by adding a string under the scheme key: ```json { "expo": { - "scheme": "mychat" + "scheme": "example" } } ``` @@ -134,14 +134,14 @@ Next, let's configure our navigation container to extract the path from the app' ```js const SimpleApp = createAppContainer(createStackNavigator({...})); -const prefix = 'mychat://'; +const prefix = 'example://'; const MainApp = () => ; ``` ### iOS -Let's configure the native iOS app to open based on the `mychat://` URI scheme. +Let's configure the native iOS app to open based on the `example://` URI scheme. In `SimpleApp/ios/SimpleApp/AppDelegate.m`: @@ -159,7 +159,7 @@ In `SimpleApp/ios/SimpleApp/AppDelegate.m`: In Xcode, open the project at `SimpleApp/ios/SimpleApp.xcodeproj`. Select the project in sidebar and navigate to the info tab. Scroll down to "URL Types" and add one. In the new URL type, set the identifier and the URL scheme to your desired URL scheme. -![Xcode project info URL types with mychat added](/assets/deep-linking/xcode-linking.png) +![Xcode project info URL types with example added](/assets/deep-linking/xcode-linking.png) Now you can press play in Xcode, or re-build on the command line: @@ -170,10 +170,10 @@ react-native run-ios To test the URI on the simulator, run the following: ```bash -xcrun simctl openurl booted mychat://chat/Eric +xcrun simctl openurl booted example://chat/Eric ``` -To test the URI on a real device, open Safari and type `mychat://chat/Eric`. +To test the URI on a real device, open Safari and type `example://chat/Eric`. ### Android @@ -196,7 +196,7 @@ In `SimpleApp/android/app/src/main/AndroidManifest.xml`, do these followings adj - + ``` @@ -210,7 +210,7 @@ react-native run-android To test the intent handling in Android, run the following: ```bash -adb shell am start -W -a android.intent.action.VIEW -d "mychat://chat/Eric" com.simpleapp +adb shell am start -W -a android.intent.action.VIEW -d "example://chat/Eric" com.simpleapp ``` ## Disable deep linking diff --git a/versioned_docs/version-5.x/configuring-links.md b/versioned_docs/version-5.x/configuring-links.md index 4542bece96..01ed72fd7b 100644 --- a/versioned_docs/version-5.x/configuring-links.md +++ b/versioned_docs/version-5.x/configuring-links.md @@ -106,7 +106,7 @@ const config = { }; const linking = { - prefixes: ['https://mychat.com/', 'mychat://'], + prefixes: ['https://example.com/', 'example://'], config, }; @@ -616,7 +616,7 @@ Example: ```js const linking = { - prefixes: ['https://mychat.com/', 'mychat://'], + prefixes: ['https://example.com/', 'example://'], config: { screens: { Chat: 'feed/:sort', diff --git a/versioned_docs/version-5.x/contributing.md b/versioned_docs/version-5.x/contributing.md index aa31a3cb0b..f90dd75053 100755 --- a/versioned_docs/version-5.x/contributing.md +++ b/versioned_docs/version-5.x/contributing.md @@ -15,23 +15,9 @@ Here are some of the ways to contribute to the project: - [Bug Fixes](#bug-fixes) - [Suggesting a Feature](#suggesting-a-feature) - [Big Pull Requests](#big-pull-requests) -- [Information](#information) - - [Issue Template](#issue-template) - - [Pull Request Template](#pull-request-template) - - [Forking the Repository](#forking-the-repository) - - [Code Review Guidelines](#code-review-guidelines) - - [Run the Example App](#run-the-example-app) - - [Run Tests](#run-tests) And here are a few helpful resources to aid in getting started: -- [Contributing](#contributing) - - [Reporting Bugs](#reporting-bugs) - - [Improving the Documentation](#improving-the-documentation) - - [Responding to Issues](#responding-to-issues) - - [Bug Fixes](#bug-fixes) - - [Suggesting a Feature](#suggesting-a-feature) - - [Big Pull Requests](#big-pull-requests) - [Information](#information) - [Issue Template](#issue-template) - [Pull Request Template](#pull-request-template) @@ -47,7 +33,7 @@ And here are a few helpful resources to aid in getting started: You can't write code without writing the occasional bug. Especially as React Navigation is moving quickly, bugs happen. When you think you've found one here's what to do: 1. Search the existing issues for one like what you're seeing. If you see one, add a 👍 reaction (please no +1 comments). Read through the comments and see if you can provide any more valuable information to the thread -2. If there are no other issues like yours then create a new one. Be sure to follow the [issue template](https://github.com/react-navigation/react-navigation/blob/main/.github/ISSUE_TEMPLATE.md). +2. If there are no other issues like yours then create a new one. Be sure to follow the [issue template](https://github.com/react-navigation/react-navigation/blob/%40react-navigation/core%405.14.4/.github/ISSUE_TEMPLATE/bug-report.md). Creating a high quality reproduction is critical. Without it we likely can't fix the bug and, in an ideal situation, you'll find out that it's not actually a bug of the library but simply done incorrectly in your project. Instant bug fix! @@ -96,7 +82,7 @@ The reason we want to do this is to save everyone time. Maybe that feature alrea ### Issue Template -Before submitting an issue, please take a look at the [issue template](https://github.com/react-navigation/react-navigation/blob/main/.github/ISSUE_TEMPLATE.md) and follow it. This is in place to help everyone better understand the issue you're having and reduce the back and forth to get the necessary information. +Before submitting an issue, please take a look at the [issue template](https://github.com/react-navigation/react-navigation/blob/%40react-navigation/core%405.14.4/.github/ISSUE_TEMPLATE/bug-report.md) and follow it. This is in place to help everyone better understand the issue you're having and reduce the back and forth to get the necessary information. Yes, it takes time and effort to complete the issue template. But that's the only way to ask high quality questions that actually get responses. @@ -104,7 +90,7 @@ Would you rather take 1 minute to create an incomplete issue report and wait mon ### Pull Request Template -Much like the issue template, the [pull request template](https://github.com/react-navigation/react-navigation/blob/main/.github/PULL_REQUEST.md) lays out instructions to ensure your pull request gets reviewed in a timely manner and reduces the back and forth. Make sure to look it over before you start writing any code. +Much like the issue template, the [pull request template](https://github.com/react-navigation/react-navigation/blob/%40react-navigation/core%405.14.4/.github/PULL_REQUEST.md) lays out instructions to ensure your pull request gets reviewed in a timely manner and reduces the back and forth. Make sure to look it over before you start writing any code. ### Forking the Repository diff --git a/versioned_docs/version-5.x/deep-linking.md b/versioned_docs/version-5.x/deep-linking.md index 3bbc811567..bbc447119a 100755 --- a/versioned_docs/version-5.x/deep-linking.md +++ b/versioned_docs/version-5.x/deep-linking.md @@ -15,12 +15,12 @@ Below, we'll go through required configurations for each platform so that the de ## Set up with Expo projects -First, you will want to specify a URL scheme for your app. This corresponds to the string before `://` in a URL, so if your scheme is `mychat` then a link to your app would be `mychat://`. The scheme only applies to standalone apps and you need to re-build the standalone app for the change to take effect. 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`. If you want to test with your custom scheme you will need to run `expo build:ios -t simulator` or `expo build:android` and install the resulting binaries in your emulators. You can register for a scheme in your `app.json` by adding a string under the scheme key: +First, you will want to specify a URL scheme for your app. This corresponds to the string before `://` in a URL, so if your scheme is `example` then a link to your app would be `example://`. The scheme only applies to standalone apps and you need to re-build the standalone app for the change to take effect. 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`. If you want to test with your custom scheme you will need to run `expo build:ios -t simulator` or `expo build:android` and install the resulting binaries in your emulators. You can register for a scheme in your `app.json` by adding a string under the scheme key: ```json { "expo": { - "scheme": "mychat" + "scheme": "example" } } ``` @@ -143,7 +143,7 @@ Read the [Expo linking guide](https://docs.expo.io/versions/latest/guides/linkin ### iOS -Let's configure the native iOS app to open based on the `mychat://` URI scheme. +Let's configure the native iOS app to open based on the `example://` URI scheme. You'll need to link `RCTLinking` to your project by following the steps described here. To be able to listen to incoming app links, you'll need to add the following lines to `SimpleApp/ios/SimpleApp/AppDelegate.m`. @@ -192,11 +192,11 @@ If your app is using Universal Links, you'll need to add the following code as w Now you need to add the scheme to your project configuration. -The easiest way to do this is with the `uri-scheme` package: `npx uri-scheme add mychat --ios`. +The easiest way to do this is with the `uri-scheme` package: `npx uri-scheme add example --ios`. If you want to do it manually, open the project at `SimpleApp/ios/SimpleApp.xcodeproj` in Xcode. Select the project in sidebar and navigate to the info tab. Scroll down to "URL Types" and add one. In the new URL type, set the identifier and the URL scheme to your desired URL scheme. -![Xcode project info URL types with mychat added](/assets/deep-linking/xcode-linking.png) +![Xcode project info URL types with example added](/assets/deep-linking/xcode-linking.png) Now you can press play in Xcode, or re-build on the command line: @@ -207,22 +207,22 @@ npx react-native run-ios To test the URI on the simulator, run the following: ```bash -npx uri-scheme open mychat://chat/jane --ios +npx uri-scheme open example://chat/jane --ios ``` or use `xcrun` directly: ```bash -xcrun simctl openurl booted mychat://chat/jane +xcrun simctl openurl booted example://chat/jane ``` -To test the URI on a real device, open Safari and type `mychat://chat/jane`. +To test the URI on a real device, open Safari and type `example://chat/jane`. ### Android To configure the external linking in Android, you can create a new intent in the manifest. -The easiest way to do this is with the `uri-scheme` package: `npx uri-scheme add mychat --android`. +The easiest way to do this is with the `uri-scheme` package: `npx uri-scheme add example --android`. If you want to add it manually, open up `SimpleApp/android/app/src/main/AndroidManifest.xml`, and make the following adjustments: @@ -241,7 +241,7 @@ If you want to add it manually, open up `SimpleApp/android/app/src/main/AndroidM - + ``` @@ -255,13 +255,13 @@ react-native run-android To test the intent handling in Android, run the following: ```bash -npx uri-scheme open mychat://chat/jane --android +npx uri-scheme open example://chat/jane --android ``` or use `adb` directly: ```bash -adb shell am start -W -a android.intent.action.VIEW -d "mychat://chat/jane" com.simpleapp +adb shell am start -W -a android.intent.action.VIEW -d "example://chat/jane" com.simpleapp ``` ## Hybrid React Native and native iOS Applications (skip for React-Native-only projects) diff --git a/versioned_docs/version-5.x/navigation-container.md b/versioned_docs/version-5.x/navigation-container.md index d58bef4935..885477f1d1 100644 --- a/versioned_docs/version-5.x/navigation-container.md +++ b/versioned_docs/version-5.x/navigation-container.md @@ -199,7 +199,7 @@ import { NavigationContainer } from '@react-navigation/native'; function App() { const linking = { - prefixes: ['https://mychat.com/', 'mychat://'], + prefixes: ['https://example.com/', 'example://'], config: { screens: { Home: 'feed/:sort', @@ -230,7 +230,7 @@ Example: ```js !url.includes('+expo-auth-session'), }; ``` @@ -149,7 +149,7 @@ const config = { }; const linking = { - prefixes: ['https://mychat.com/', 'mychat://'], + prefixes: ['https://example.com/', 'example://'], config, }; @@ -679,7 +679,7 @@ Example: ```js const linking = { - prefixes: ['https://mychat.com/', 'mychat://'], + prefixes: ['https://example.com/', 'example://'], config: { screens: { Chat: 'feed/:sort', diff --git a/versioned_docs/version-6.x/contributing.md b/versioned_docs/version-6.x/contributing.md index aa31a3cb0b..461dde2dcc 100755 --- a/versioned_docs/version-6.x/contributing.md +++ b/versioned_docs/version-6.x/contributing.md @@ -15,23 +15,9 @@ Here are some of the ways to contribute to the project: - [Bug Fixes](#bug-fixes) - [Suggesting a Feature](#suggesting-a-feature) - [Big Pull Requests](#big-pull-requests) -- [Information](#information) - - [Issue Template](#issue-template) - - [Pull Request Template](#pull-request-template) - - [Forking the Repository](#forking-the-repository) - - [Code Review Guidelines](#code-review-guidelines) - - [Run the Example App](#run-the-example-app) - - [Run Tests](#run-tests) And here are a few helpful resources to aid in getting started: -- [Contributing](#contributing) - - [Reporting Bugs](#reporting-bugs) - - [Improving the Documentation](#improving-the-documentation) - - [Responding to Issues](#responding-to-issues) - - [Bug Fixes](#bug-fixes) - - [Suggesting a Feature](#suggesting-a-feature) - - [Big Pull Requests](#big-pull-requests) - [Information](#information) - [Issue Template](#issue-template) - [Pull Request Template](#pull-request-template) @@ -47,7 +33,7 @@ And here are a few helpful resources to aid in getting started: You can't write code without writing the occasional bug. Especially as React Navigation is moving quickly, bugs happen. When you think you've found one here's what to do: 1. Search the existing issues for one like what you're seeing. If you see one, add a 👍 reaction (please no +1 comments). Read through the comments and see if you can provide any more valuable information to the thread -2. If there are no other issues like yours then create a new one. Be sure to follow the [issue template](https://github.com/react-navigation/react-navigation/blob/main/.github/ISSUE_TEMPLATE.md). +2. If there are no other issues like yours then create a new one. Be sure to follow the [issue template](https://github.com/react-navigation/react-navigation/blob/6.x/.github/ISSUE_TEMPLATE/bug-report.yml). Creating a high quality reproduction is critical. Without it we likely can't fix the bug and, in an ideal situation, you'll find out that it's not actually a bug of the library but simply done incorrectly in your project. Instant bug fix! @@ -96,7 +82,7 @@ The reason we want to do this is to save everyone time. Maybe that feature alrea ### Issue Template -Before submitting an issue, please take a look at the [issue template](https://github.com/react-navigation/react-navigation/blob/main/.github/ISSUE_TEMPLATE.md) and follow it. This is in place to help everyone better understand the issue you're having and reduce the back and forth to get the necessary information. +Before submitting an issue, please take a look at the [issue template](https://github.com/react-navigation/react-navigation/blob/6.x/.github/ISSUE_TEMPLATE/bug-report.yml) and follow it. This is in place to help everyone better understand the issue you're having and reduce the back and forth to get the necessary information. Yes, it takes time and effort to complete the issue template. But that's the only way to ask high quality questions that actually get responses. @@ -104,7 +90,7 @@ Would you rather take 1 minute to create an incomplete issue report and wait mon ### Pull Request Template -Much like the issue template, the [pull request template](https://github.com/react-navigation/react-navigation/blob/main/.github/PULL_REQUEST.md) lays out instructions to ensure your pull request gets reviewed in a timely manner and reduces the back and forth. Make sure to look it over before you start writing any code. +Much like the issue template, the [pull request template](https://github.com/react-navigation/react-navigation/blob/6.x/.github/PULL_REQUEST_TEMPLATE.md) lays out instructions to ensure your pull request gets reviewed in a timely manner and reduces the back and forth. Make sure to look it over before you start writing any code. ### Forking the Repository diff --git a/versioned_docs/version-6.x/deep-linking.md b/versioned_docs/version-6.x/deep-linking.md index ef0f3ee0e7..e112f3f7f8 100755 --- a/versioned_docs/version-6.x/deep-linking.md +++ b/versioned_docs/version-6.x/deep-linking.md @@ -17,12 +17,12 @@ Below, we'll go through required configurations so that the deep link integratio ## Setup with Expo projects -First, you will want to specify a URL scheme for your app. This corresponds to the string before `://` in a URL, so if your scheme is `mychat` then a link to your app would be `mychat://`. You can register for a scheme in your `app.json` by adding a string under the scheme key: +First, you will want to specify a URL scheme for your app. This corresponds to the string before `://` in a URL, so if your scheme is `example` then a link to your app would be `example://`. You can register for a scheme in your `app.json` by adding a string under the scheme key: ```json { "expo": { - "scheme": "mychat" + "scheme": "example" } } ``` @@ -69,7 +69,7 @@ const linking = { ### Setup on iOS -Let's configure the native iOS app to open based on the `mychat://` URI scheme. +Let's configure the native iOS app to open based on the `example://` URI scheme. You'll need to link `RCTLinking` to your project by following the steps described here. To be able to listen to incoming app links, you'll need to add the following lines to `AppDelegate.m` in your project: @@ -104,12 +104,12 @@ Now you need to add the scheme to your project configuration. The easiest way to do this is with the `uri-scheme` package by running the following: ```bash -npx uri-scheme add mychat --ios +npx uri-scheme add example --ios ``` If you want to do it manually, open the project (e.g. `SimpleApp/ios/SimpleApp.xcworkspace`) in Xcode. Select the project in sidebar and navigate to the info tab. Scroll down to "URL Types" and add one. In the new URL type, set the identifier and the URL scheme to your desired URL scheme. -![Xcode project info URL types with mychat added](/assets/deep-linking/xcode-linking.png) +![Xcode project info URL types with example added](/assets/deep-linking/xcode-linking.png) To make sure Universal Links work in your app, you also need to setup [Associated Domains](https://developer.apple.com/documentation/Xcode/supporting-associated-domains) on your server. @@ -129,7 +129,7 @@ If you're using React Navigation within a hybrid app - an iOS app that has both To configure the external linking in Android, you can create a new intent in the manifest. -The easiest way to do this is with the `uri-scheme` package: `npx uri-scheme add mychat --android`. +The easiest way to do this is with the `uri-scheme` package: `npx uri-scheme add example --android`. If you want to add it manually, open up `SimpleApp/android/app/src/main/AndroidManifest.xml`, and make the following adjustments: @@ -148,7 +148,7 @@ If you want to add it manually, open up `SimpleApp/android/app/src/main/AndroidM - + ``` @@ -172,7 +172,7 @@ After adding them, it should look like this: - + @@ -218,7 +218,7 @@ npx uri-scheme open [your deep link] --[ios|android] For example: ```bash -npx uri-scheme open "mychat://chat/jane" --ios +npx uri-scheme open "example://chat/jane" --ios ``` Or if using Expo client: @@ -238,7 +238,7 @@ xcrun simctl openurl booted [your deep link] For example: ```bash -xcrun simctl openurl booted "mychat://chat/jane" +xcrun simctl openurl booted "example://chat/jane" ``` ### Testing with `adb` on Android @@ -252,7 +252,7 @@ adb shell am start -W -a android.intent.action.VIEW -d [your deep link] [your an For example: ```bash -adb shell am start -W -a android.intent.action.VIEW -d "mychat://chat/jane" com.simpleapp +adb shell am start -W -a android.intent.action.VIEW -d "example://chat/jane" com.simpleapp ``` Or if using Expo client: diff --git a/versioned_docs/version-6.x/native-stack-navigator.md b/versioned_docs/version-6.x/native-stack-navigator.md index 3aa0c89c91..4b286b0ac7 100755 --- a/versioned_docs/version-6.x/native-stack-navigator.md +++ b/versioned_docs/version-6.x/native-stack-navigator.md @@ -457,15 +457,17 @@ Only supported on Android and iOS. #### `statusBarStyle` -Sets the status bar color (similar to the `StatusBar` component). Defaults to `auto`. +Sets the status bar color (similar to the `StatusBar` component). Supported values: -- `"auto"` +- `"auto"` (iOS only) - `"inverted"` (iOS only) - `"dark"` - `"light"` +Defaults to `auto` on iOS and `light` on Android. + Requires setting `View controller-based status bar appearance -> YES` (or removing the config) in your `Info.plist` file. Only supported on Android and iOS. diff --git a/versioned_docs/version-6.x/navigation-container.md b/versioned_docs/version-6.x/navigation-container.md index f5708d8a60..5d48860e80 100644 --- a/versioned_docs/version-6.x/navigation-container.md +++ b/versioned_docs/version-6.x/navigation-container.md @@ -237,7 +237,7 @@ import { NavigationContainer } from '@react-navigation/native'; function App() { const linking = { - prefixes: ['https://mychat.com/', 'mychat://'], + prefixes: ['https://example.com/', 'example://'], config: { screens: { Home: 'feed/:sort', @@ -268,7 +268,7 @@ Example: ```js -To do this, we need a couple of things: - -1. Define two hooks: `useIsSignedIn` and `useIsSignedOut`, which return a boolean value indicating whether the user is signed in or not. -2. Use the `useIsSignedIn` and `useIsSignedOut` along with the [`if`](static-configuration.md#if) property to define the screens that are available based on the condition. - -This tells React Navigation to show specific screens based on the signed in status. When the signed in status changes, React Navigation will automatically show the appropriate screen. - -## Define the hooks - -To implement the `useIsSignedIn` and `useIsSignedOut` hooks, we can start by creating a context to store the authentication state. Let's call it `SignInContext`: - -```js -import * as React from 'react'; - -const SignInContext = React.createContext(); -``` - -Then we can implement the `useIsSignedIn` and `useIsSignedOut` hooks as follows: - -```js -function useIsSignedIn() { - const isSignedIn = React.useContext(SignInContext); - return isSignedIn; -} - -function useIsSignedOut() { - const isSignedIn = React.useContext(SignInContext); - return !isSignedIn; -} -``` - -We'll discuss how to expose the context value later. - -```js name="Customizing tabs appearance" snack +```js name="Authentication flow" snack import * as React from 'react'; import { View } from 'react-native'; import { createStaticNavigation } from '@react-navigation/native'; @@ -79,40 +46,19 @@ const useIsSignedIn = () => { }; const useIsSignedOut = () => { - return false; + return !useIsSignedIn(); }; -const signedInStack = createNativeStackNavigator({ - screens: { - Home: HomeScreen, - Profile: ProfileScreen, - Settings: SettingsScreen, - }, -}); - -const signedOutStack = createNativeStackNavigator({ - screens: { - SignIn: SignInScreen, - SignUp: SignUpScreen, - }, -}); - // codeblock-focus-start const RootStack = createNativeStackNavigator({ screens: { - LoggedIn: { + Home: { if: useIsSignedIn, - screen: signedInStack, - options: { - headerShown: false, - }, + screen: HomeScreen, }, - LoggedOut: { + SignIn: { if: useIsSignedOut, - screen: signedOutStack, - options: { - headerShown: false, - }, + screen: SignInScreen, }, }, }); @@ -128,29 +74,59 @@ function HomeScreen() { return ; } -function ProfileScreen() { +function SignInScreen() { return ; } +``` -function SettingsScreen() { - return ; -} +Here, for each screen, we have defined a condition using the `if` property which takes a hook. The hook returns a boolean value indicating whether the user is signed in or not. If the hook returns `true`, the screen will be available, otherwise it won't. -function SignInScreen() { - return ; +This means: + +- When `useIsSignedIn` returns `true`, React Navigation will only use the `Home` screen, since it's the only screen matching the condition. +- Similarly, when `useIsSignedOut` returns `true`, React Navigation will use the `SignIn` screen. + +This makes it impossible to navigate to the `Home` when the user is not signed in, and to `SignIn` when the user is signed in. + +When the values returned by `useIsSignedin` and `useIsSignedOut` change, the screens matching the condition will change: + +- Let's say, initially `useIsSignedOut` returns `true`. This means that `SignIn` screens is shown. +- After the user signs in, the return value of `useIsSignedIn` will change to `true` and `useIsSignedOut` will change to `false`, which means: + - React Navigation will see that the `SignIn` screen is no longer matches the condition, so it will remove the screen. + - Then it'll show the `Home` screen automatically because that's the first screen available when `useIsSignedIn` returns `true`. + +The order of the screens matters when there are multiple screens matching the condition. For example, if there are two screens matching `useIsSignedIn`, the first screen will be shown when the condition is `true`. + +## Define the hooks + +To implement the `useIsSignedIn` and `useIsSignedOut` hooks, we can start by creating a context to store the authentication state. Let's call it `SignInContext`: + +```js +import * as React from 'react'; + +const SignInContext = React.createContext(); +``` + +Then we can implement the `useIsSignedIn` and `useIsSignedOut` hooks as follows: + +```js +function useIsSignedIn() { + const isSignedIn = React.useContext(SignInContext); + return isSignedIn; } -function SignUpScreen() { - return ; +function useIsSignedOut() { + return !useIsSignedIn(); } ``` +We'll discuss how to provide the context value later. + - -For example: + -```js name="Customizing tabs appearance" snack +```js name="Authentication flow" snack import * as React from 'react'; import { View } from 'react-native'; import { NavigationContainer } from '@react-navigation/native'; @@ -158,29 +134,17 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack'; const Stack = createNativeStackNavigator(); -const getIsSignedIn = () => { - // custom logic - return true; -}; - export default function App() { - const isSignedIn = getIsSignedIn(); + const isSignedIn = true; return ( // codeblock-focus-start {isSignedIn ? ( - <> - - - - + ) : ( - <> - - - + )} // codeblock-focus-end @@ -192,39 +156,35 @@ function HomeScreen() { return ; } -function ProfileScreen() { - return ; -} - -function SettingsScreen() { - return ; -} - function SignInScreen() { return ; } - -function SignUpScreen() { - return ; -} ``` -When we define screens like this, when `isSignedIn` is `true`, React Navigation will only see the `Home`, `Profile` and `Settings` screens, and when it's `false`, React Navigation will see the `SignIn` and `SignUp` screens. This makes it impossible to navigate to the `Home`, `Profile` and `Settings` screens when the user is not signed in, and to `SignIn` and `SignUp` screens when the user is signed in. +Here, we have conditionally defined the screens based on the value of `isSignedIn`. -This pattern has been in use by other routing libraries such as React Router for a long time, and is commonly known as "Protected routes". Here, our screens which need the user to be signed in are "protected" and cannot be navigated to by other means if the user is not signed in. +This means: -The magic happens when the value of the `isSignedIn` variable changes. Let's say, initially `isSignedIn` is `false`. This means, either `SignIn` or `SignUp` screens are shown. After the user signs in, the value of `isSignedIn` will change to `true`. React Navigation will see that the `SignIn` and `SignUp` screens are no longer defined and so it will remove them. Then it'll show the `Home` screen automatically because that's the first screen defined when `isSignedIn` is `true`. +- When `isSignedIn` is `true`, React Navigation will only see the `Home` screen, since it's the only screen defined based on the condition. +- Similarly, when `isSignedIn` is `false`, React Navigation will only see the `SignIn` screen. -The example shows stack navigator, but you can use the same approach with any navigator. +This makes it impossible to navigate to the `Home` when the user is not signed in, and to `SignIn` when the user is signed in. -By conditionally defining different screens based on a variable, we can implement auth flow in a simple way that doesn't require additional logic to make sure that the correct screen is shown. +When the value of `isSignedin` changes, the screens defined based on the condition will change: + +- Let's say, initially `isSignedin` is `false`. This means that `SignIn` screens is shown. +- After the user signs in, the value of `isSignedin` will change to `true`, which means: + - React Navigation will see that the `SignIn` screen is no longer defined, so it will remove the screen. + - Then it'll show the `Home` screen automatically because that's the first screen defined when `isSignedin` returns `true`. + +The order of the screens matters when there are multiple screens matching the condition. For example, if there are two screens defined based on `isSignedin`, the first screen will be shown when the condition is `true`. -## Define our screens +## Add more screens -In our navigator, we can conditionally define appropriate screens. For our case, let's say we have 3 screens: +For our case, let's say we have 3 screens: - `SplashScreen` - This will show a splash or loading screen when we're restoring the token. - `SignIn` - This is the screen we show if the user isn't signed in already (we couldn't find a token). @@ -255,10 +215,46 @@ const RootStack = createNativeStackNavigator({ const Navigation = createStaticNavigation(RootStack); ``` + + + +```js +const Stack = createNativeStackNavigator(); + +export default function App() { + const isSignedIn = true; + + return ( + + + {isSignedIn ? ( + + ) : ( + + )} + + + ); +} +``` + + + + Notice how we have only defined the `Home` and `SignIn` screens here, and not the `SplashScreen`. The `SplashScreen` should be rendered before we render any navigators so that we don't render incorrect screens before we know whether the user is signed in or not. When we use this in our component, it'd look something like this: + + + ```js if (isLoading) { // We haven't finished checking for the token yet @@ -274,43 +270,6 @@ return ( ); ``` -In the above snippet, `isLoading` means that we're still checking if we have a token. This can usually be done by checking if we have a token in `SecureStore` and validating the token. After we get the token and if it's valid, we need to set the `userToken`. We also have another state called `isSignout` to have a different animation on sign out. - -Next, we're exposing the sign in status via the `SignInContext` so that it's available to the `useIsSignedIn` and `useIsSignedOut` hooks. - -In the above example, we have one screen for each case. But you could also define multiple screens. For example, you probably want to define password reset, signup, etc screens as well when the user isn't signed in. Similarly for the screens accessible after sign in, you probably have more than one screen. We can use [`groups`](static-configuration.md#groups) to define multiple screens: - -```js -const RootStack = createNativeStackNavigator({ - screens: { - // Common screens - }, - groups: { - SignedIn: { - if: useIsSignedIn, - screens: { - Home: HomeScreen, - Profile: ProfileScreen, - }, - }, - SignedOut: { - if: useIsSignedOut, - screens: { - SignIn: SignInScreen, - SignUp: SignUpScreen, - ResetPassword: ResetPasswordScreen, - }, - }, - }, -}); -``` - -:::tip - -If you have both your login-related screens and rest of the screens in Stack navigators, we recommend to use a single Stack navigator and place the conditional inside instead of using 2 different navigators. This makes it possible to have a proper transition animation during login/logout. - -::: - @@ -320,11 +279,12 @@ if (isLoading) { return ; } +const isSignedIn = userToken != null; + return ( - {userToken == null ? ( - // No token found, user isn't signed in + {isSignedIn ? ( ) : ( - // User is signed in )} @@ -345,51 +304,49 @@ return ( -In the above snippet, `isLoading` means that we're still checking if we have a token. This can usually be done by checking if we have a token in `SecureStore` and validating the token. After we get the token and if it's valid, we need to set the `userToken`. We also have another state called `isSignout` to have a different animation on sign out. - -The main thing to notice is that we're conditionally defining screens based on these state variables: +In the above snippet, `isLoading` means that we're still checking if we have a token. This can usually be done by checking if we have a token in `SecureStore` and validating the token. -- `SignIn` screen is only defined if `userToken` is `null` (user is not signed in) -- `Home` screen is only defined if `userToken` is non-null (user is signed in) +Next, we're exposing the sign in status via the `SignInContext` so that it's available to the `useIsSignedIn` and `useIsSignedOut` hooks. -Here, we're conditionally defining one screen for each case. But you could also define multiple screens. For example, you probably want to define password reset, signup, etc screens as well when the user isn't signed in. Similarly, for the screens accessible after signing in, you probably have more than one screen. We can use `React.Fragment` to define multiple screens: +In the above example, we have one screen for each case. But you could also define multiple screens. For example, you probably want to define password reset, signup, etc screens as well when the user isn't signed in. Similarly for the screens accessible after sign in, you probably have more than one screen. -```js -const SignInContext = React.createContext(); - -function useIsSignedIn() { - const isSignedIn = React.useContext(SignInContext); - return isSignedIn; -} - -function useIsSignedOut() { - const isSignedIn = React.useContext(SignInContext); - return !isSignedIn; -} - -/* content */ - -export default function App() { - /* content */ +We can use [`groups`](static-configuration.md#groups) to define multiple screens: - const isSignedIn = userToken != null; - - return ( - - - - ); -} +```js +const RootStack = createNativeStackNavigator({ + screens: { + // Common screens + }, + groups: { + SignedIn: { + if: useIsSignedIn, + screens: { + Home: HomeScreen, + Profile: ProfileScreen, + }, + }, + SignedOut: { + if: useIsSignedOut, + screens: { + SignIn: SignInScreen, + SignUp: SignUpScreen, + ResetPassword: ResetPasswordScreen, + }, + }, + }, +}); ``` +We can use [`React.Fragment`](https://react.dev/reference/react/Fragment) or [`Group`](group.md) to define multiple screens: + ```js -state.userToken == null ? ( +isSignedIn ? ( <> @@ -405,7 +362,7 @@ state.userToken == null ? ( :::tip -If you have both your login-related screens and rest of the screens in two different Stack navigators, we recommend to use a single Stack navigator and place the conditional inside instead of using 2 different navigators. This makes it possible to have a proper transition animation during login/logout. +Instead of having your login-related screens and rest of the screens in two different Stack navigators and render them conditionally, we recommend to use a single Stack navigator and place the conditional inside. This makes it possible to have a proper transition animation during login/logout. ::: @@ -422,8 +379,8 @@ The following is just an example of how you might implement the logic for authen From the previous snippet, we can see that we need 3 state variables: -- `isLoading` - We set this to `true` when we're trying to check if we already have a token saved in `SecureStore` -- `isSignout` - We set this to `true` when user is signing out, otherwise set it to `false` +- `isLoading` - We set this to `true` when we're trying to check if we already have a token saved in `SecureStore`. +- `isSignout` - We set this to `true` when user is signing out, otherwise set it to `false`. This can be used to customize the animation when signing out. - `userToken` - The token for the user. If it's non-null, we assume the user is logged in, otherwise not. So we need to: @@ -472,8 +429,7 @@ function useIsSignedIn() { } function useIsSignedOut() { - const isSignedIn = React.useContext(SignInContext); - return !isSignedIn; + return !useIsSignedIn(); } function SplashScreen() { @@ -619,11 +575,11 @@ const RootStack = createNativeStackNavigator({ screen: HomeScreen, }, SignIn: { + if: useIsSignedOut, screen: SignInScreen, options: { title: 'Sign in', }, - if: useIsSignedOut, }, }, }); @@ -975,6 +931,10 @@ If you have a bunch of shared screens, you can also use [`navigationKey` with a +The examples above show stack navigator, but you can use the same approach with any navigator. + +By specifying a condition for our screens, we can implement auth flow in a simple way that doesn't require additional logic to make sure that the correct screen is shown. + ## Don't manually navigate when conditionally rendering screens It's important to note that when using such a setup, you **don't manually navigate** to the `Home` screen by calling `navigation.navigate('Home')` or any other method. **React Navigation will automatically navigate to the correct screen** when `isSignedIn` changes - `Home` screen when `isSignedIn` becomes `true`, and to `SignIn` screen when `isSignedIn` becomes `false`. You'll get an error if you attempt to navigate manually. diff --git a/versioned_docs/version-7.x/bottom-tab-navigator.md b/versioned_docs/version-7.x/bottom-tab-navigator.md index d4c4297750..a11dba22b2 100755 --- a/versioned_docs/version-7.x/bottom-tab-navigator.md +++ b/versioned_docs/version-7.x/bottom-tab-navigator.md @@ -152,6 +152,7 @@ It supports the following values: - `initialRoute` - return to initial screen passed in `initialRouteName` prop, if not passed, defaults to the first screen - `order` - return to screen defined before the focused screen - `history` - return to last visited screen in the navigator; if the same screen is visited multiple times, the older entries are dropped from the history +- `fullHistory` - return to last visited screen in the navigator; doesn't drop duplicate entries unlike `history` - this behavior is useful to match how web pages work - `none` - do not handle back button #### `detachInactiveScreens` diff --git a/versioned_docs/version-7.x/combine-static-with-dynamic.md b/versioned_docs/version-7.x/combine-static-with-dynamic.md index 6b9c738e35..bfb8c80b61 100644 --- a/versioned_docs/version-7.x/combine-static-with-dynamic.md +++ b/versioned_docs/version-7.x/combine-static-with-dynamic.md @@ -198,7 +198,7 @@ import { createPathConfigForStaticNavigation } from '@react-navigation/native'; const feedScreens = createPathConfigForStaticNavigation(FeedTabs); const linking = { - prefixes: ['https://mychat.com/', 'mychat://'], + prefixes: ['https://example.com/', 'example://'], config: { screens: { Home: '', diff --git a/versioned_docs/version-7.x/configuring-links.md b/versioned_docs/version-7.x/configuring-links.md index 1f68858f90..7b98cbee23 100644 --- a/versioned_docs/version-7.x/configuring-links.md +++ b/versioned_docs/version-7.x/configuring-links.md @@ -92,13 +92,13 @@ You can also pass a [`fallback`](navigation-container.md#fallback) prop that con ## Prefixes -The `prefixes` option can be used to specify custom schemes (e.g. `mychat://`) as well as host & domain names (e.g. `https://mychat.com`) if you have configured [Universal Links](https://developer.apple.com/ios/universal-links/) or [Android App Links](https://developer.android.com/training/app-links). +The `prefixes` option can be used to specify custom schemes (e.g. `example://`) as well as host & domain names (e.g. `https://example.com`) if you have configured [Universal Links](https://developer.apple.com/ios/universal-links/) or [Android App Links](https://developer.android.com/training/app-links). For example: ```js const linking = { - prefixes: ['mychat://', 'https://mychat.com/'], + prefixes: ['example://', 'https://example.com/'], }; ``` @@ -106,11 +106,11 @@ Note that the `prefixes` option is not supported on Web. The host & domain names ### Multiple subdomains​ -To match all subdomains of an associated domain, you can specify a wildcard by prefixing `*`. before the beginning of a specific domain. Note that an entry for `*.mychat.com` does not match `mychat.com` because of the period after the asterisk. To enable matching for both `*.mychat.com` and `mychat.com`, you need to provide a separate prefix entry for each. +To match all subdomains of an associated domain, you can specify a wildcard by prefixing `*`. before the beginning of a specific domain. Note that an entry for `*.example.com` does not match `example.com` because of the period after the asterisk. To enable matching for both `*.example.com` and `example.com`, you need to provide a separate prefix entry for each. ```js const linking = { - prefixes: ['mychat://', 'https://mychat.com/', 'https://*.mychat.com/'], + prefixes: ['example://', 'https://example.com/', 'https://*.example.com/'], }; ``` @@ -122,7 +122,7 @@ To achieve this, you can use the `filter` option: ```js const linking = { - prefixes: ['mychat://', 'https://mychat.com/'], + prefixes: ['example://', 'https://example.com/'], // highlight-next-line filter: (url) => !url.includes('+expo-auth-session'), }; @@ -132,11 +132,11 @@ This is not supported on Web as we always need to handle the URL of the page. ## Apps under subpaths -If your app is hosted under a subpath, you can specify the subpath at the top-level of the `config`. For example, if your app is hosted at `https://mychat.com/app`, you can specify the `path` as `app`: +If your app is hosted under a subpath, you can specify the subpath at the top-level of the `config`. For example, if your app is hosted at `https://example.com/app`, you can specify the `path` as `app`: ```js const linking = { - prefixes: ['mychat://', 'https://mychat.com/'], + prefixes: ['example://', 'https://example.com/'], config: { // highlight-next-line path: 'app', @@ -322,7 +322,7 @@ const config = { }; const linking = { - prefixes: ['https://mychat.com/', 'mychat://'], + prefixes: ['https://example.com/', 'example://'], config, }; @@ -1357,7 +1357,7 @@ Example: ```js const linking = { - prefixes: ['https://mychat.com/', 'mychat://'], + prefixes: ['https://example.com/', 'example://'], getStateFromPath: (path, options) => { // Return a state object here // You can also reuse the default logic by importing `getStateFromPath` from `@react-navigation/native` diff --git a/versioned_docs/version-7.x/contributing.md b/versioned_docs/version-7.x/contributing.md index aa31a3cb0b..ec05c128ca 100755 --- a/versioned_docs/version-7.x/contributing.md +++ b/versioned_docs/version-7.x/contributing.md @@ -15,23 +15,9 @@ Here are some of the ways to contribute to the project: - [Bug Fixes](#bug-fixes) - [Suggesting a Feature](#suggesting-a-feature) - [Big Pull Requests](#big-pull-requests) -- [Information](#information) - - [Issue Template](#issue-template) - - [Pull Request Template](#pull-request-template) - - [Forking the Repository](#forking-the-repository) - - [Code Review Guidelines](#code-review-guidelines) - - [Run the Example App](#run-the-example-app) - - [Run Tests](#run-tests) And here are a few helpful resources to aid in getting started: -- [Contributing](#contributing) - - [Reporting Bugs](#reporting-bugs) - - [Improving the Documentation](#improving-the-documentation) - - [Responding to Issues](#responding-to-issues) - - [Bug Fixes](#bug-fixes) - - [Suggesting a Feature](#suggesting-a-feature) - - [Big Pull Requests](#big-pull-requests) - [Information](#information) - [Issue Template](#issue-template) - [Pull Request Template](#pull-request-template) @@ -47,7 +33,7 @@ And here are a few helpful resources to aid in getting started: You can't write code without writing the occasional bug. Especially as React Navigation is moving quickly, bugs happen. When you think you've found one here's what to do: 1. Search the existing issues for one like what you're seeing. If you see one, add a 👍 reaction (please no +1 comments). Read through the comments and see if you can provide any more valuable information to the thread -2. If there are no other issues like yours then create a new one. Be sure to follow the [issue template](https://github.com/react-navigation/react-navigation/blob/main/.github/ISSUE_TEMPLATE.md). +2. If there are no other issues like yours then create a new one. Be sure to follow the [issue template](https://github.com/react-navigation/react-navigation/blob/main/.github/ISSUE_TEMPLATE/bug-report.yml). Creating a high quality reproduction is critical. Without it we likely can't fix the bug and, in an ideal situation, you'll find out that it's not actually a bug of the library but simply done incorrectly in your project. Instant bug fix! @@ -96,7 +82,7 @@ The reason we want to do this is to save everyone time. Maybe that feature alrea ### Issue Template -Before submitting an issue, please take a look at the [issue template](https://github.com/react-navigation/react-navigation/blob/main/.github/ISSUE_TEMPLATE.md) and follow it. This is in place to help everyone better understand the issue you're having and reduce the back and forth to get the necessary information. +Before submitting an issue, please take a look at the [issue template](https://github.com/react-navigation/react-navigation/blob/main/.github/ISSUE_TEMPLATE/bug-report.yml) and follow it. This is in place to help everyone better understand the issue you're having and reduce the back and forth to get the necessary information. Yes, it takes time and effort to complete the issue template. But that's the only way to ask high quality questions that actually get responses. @@ -104,7 +90,7 @@ Would you rather take 1 minute to create an incomplete issue report and wait mon ### Pull Request Template -Much like the issue template, the [pull request template](https://github.com/react-navigation/react-navigation/blob/main/.github/PULL_REQUEST.md) lays out instructions to ensure your pull request gets reviewed in a timely manner and reduces the back and forth. Make sure to look it over before you start writing any code. +Much like the issue template, the [pull request template](https://github.com/react-navigation/react-navigation/blob/main/.github/PULL_REQUEST_TEMPLATE.md) lays out instructions to ensure your pull request gets reviewed in a timely manner and reduces the back and forth. Make sure to look it over before you start writing any code. ### Forking the Repository diff --git a/versioned_docs/version-7.x/custom-navigators.md b/versioned_docs/version-7.x/custom-navigators.md index c8567fa848..eaaf441765 100755 --- a/versioned_docs/version-7.x/custom-navigators.md +++ b/versioned_docs/version-7.x/custom-navigators.md @@ -4,26 +4,60 @@ title: Custom navigators sidebar_label: Custom navigators --- -Navigators allow you to define your application's navigation structure. Navigators also render common elements such as headers and tab bars which you can configure. +In essence, a navigator is a React component that takes a set of screens and options, and renders them based on its [navigation state](navigation-state.md), generally with additional UI such as headers, tab bars, or drawers. -Under the hood, navigators are plain React components. +React Navigation provides a few built-in navigators, but they might not always fit your needs if you want a very custom behavior or UI. In such cases, you can build your own custom navigators using React Navigation's APIs. -## Built-in Navigators +A custom navigator behaves just like a built-in navigator, and can be used in the same way. This means you can define screens the same way, use [route](route-object.md) and [navigation](navigation-object.md) objects in your screens, and navigate between screens with familiar API. The navigator will also be able to handle deep linking, state persistence, and other features that built-in navigators support. -We include some commonly needed navigators such as: +## Overview -- [`createStackNavigator`](stack-navigator.md) - Renders one screen at a time and provides transitions between screens. When a new screen is opened it is placed on top of the stack. -- [`createDrawerNavigator`](drawer-navigator.md) - Provides a drawer that slides in from the left of the screen by default. -- [`createBottomTabNavigator`](bottom-tab-navigator.md) - Renders a tab bar that lets the user switch between several screens. -- [`createMaterialTopTabNavigator`](material-top-tab-navigator.md) - Renders tab view which lets the user switch between several screens using swipe gesture or the tab bar. +Under the hood, navigators are plain React components that use the [`useNavigationBuilder`](#usenavigationbuilder) hook. -## API for building custom navigators +The navigator component then uses this state to layout the screens appropriately with any additional UI based on the use case. This component is then wrapped in [`createNavigatorFactory`](#createnavigatorfactory) to create the API for the navigator. -A navigator bundles a router and a view which takes the [navigation state](navigation-state.md) and decides how to render it. We export a `useNavigationBuilder` hook to build custom navigators that integrate with rest of React Navigation. +A very basic example looks like this: + +```js +function MyStackNavigator(props) { + const { state, descriptors, NavigationContent } = useNavigationBuilder( + StackRouter, + props + ); + + const focusedRoute = state.routes[state.index]; + const descriptor = descriptors[focusedRoute.key]; + + return {descriptor.render()}; +} + +export const createMyStackNavigator = createNavigatorFactory(MyStackNavigator); +``` + +Now, we have an already working navigator, even though it doesn't do anything special yet. + +Let's break this down: + +- We define a `MyNavigator` component that contains our navigator logic. This is the component that's rendered when you render `` in your app with the `createMyStackNavigator` factory function. +- We use the `useNavigationBuilder` hook and pass it [`StackRouter`](custom-routers.md#built-in-routers), which would make our navigator behave like a stack navigator. Any other router such as `TabRouter`, `DrawerRouter`, or a custom router can be used here as well. +- The hook returns the [navigation state](navigation-state.md) in the `state` property. This is the current state of the navigator. There's also a `descriptors` object which contains the data and helpers for each screen in the navigator. +- We get the focused route from the state with `state.routes[state.index]` - as `state.index` is the index of the currently focused route in the `state.routes` array. +- Then we get the corresponding descriptor for the focused route with `descriptors[focusedRoute.key]` and call the `render()` method on it to get the React element for the screen. +- The content of the navigator is wrapped in `NavigationContent` to provide appropriate context and wrappers. + +With this, we have a basic stack navigator that renders only the focused screen. Unlike the built-in stack navigator, this doesn't keep unfocused screens rendered. But you can loop through `state.routes` and render all of the screens if you want to keep them mounted. You can also read `descriptor.options` to get the [options](screen-options.md) to handle the screen's title, header, and other options. + +This also doesn't have any additional UI apart from the screen content. There are no gestures or animations. So you're free to add any additional UI, gestures, animations etc. as needed. You can also layout the screens in any way you want, such as rendering them side-by-side or in a grid, instead of stacking them on top of each other like the built-in stack navigator does. + +You can see a more complete example of a custom navigator later in this document. + +## API Definition ### `useNavigationBuilder` -This hook allows a component to hook into React Navigation. It accepts the following arguments: +This hook contains the core logic of a navigator, and is responsible for storing and managing the [navigation state](navigation-state.md). It takes a [router](custom-routers.md) as an argument to know how to handle various navigation actions. It then returns the state and helper methods for the navigator component to use. + +It accepts the following arguments: - `createRouter` - A factory method which returns a router object (e.g. `StackRouter`, `TabRouter`). - `options` - Options for the hook and the router. The navigator should forward its props here so that user can provide props to configure the navigator. By default, the following options are accepted: @@ -56,27 +90,9 @@ import { TabActions, } from '@react-navigation/native'; -function TabNavigator({ - id, - initialRouteName, - children, - layout, - screenListeners, - screenOptions, - screenLayout, - tabBarStyle, - contentStyle, -}) { +function TabNavigator({ tabBarStyle, contentStyle, ...rest }) { const { state, navigation, descriptors, NavigationContent } = - useNavigationBuilder(TabRouter, { - id, - initialRouteName, - children, - layout, - screenListeners, - screenOptions, - screenLayout, - }); + useNavigationBuilder(TabRouter, rest); return ( @@ -158,6 +174,12 @@ export function createMyNavigator(config) { } ``` +:::note + +We can also do `export const createMyNavigator = createNavigatorFactory(MyNavigator)` directly instead of wrapping in another function. However, the wrapper function is necessary to have proper [TypeScript support](#type-checking-navigators) for the navigator. + +::: + Then it can be used like this: ```js @@ -210,7 +232,7 @@ import { useNavigationBuilder, } from '@react-navigation/native'; -// Props accepted by the view +// Additional props accepted by the view type TabNavigationConfig = { tabBarStyle: StyleProp; contentStyle: StyleProp; @@ -222,7 +244,6 @@ type TabNavigationOptions = { }; // Map of event name and the type of data (in event.data) -// // canPreventDefault: true adds the defaultPrevented property to the // emitted events. type TabNavigationEventMap = { @@ -232,28 +253,34 @@ type TabNavigationEventMap = { }; }; +// The type of the navigation object for each screen +type TabNavigationProp< + ParamList extends ParamListBase, + RouteName extends keyof ParamList = keyof ParamList, + NavigatorID extends string | undefined = undefined, +> = NavigationProp< + ParamList, + RouteName, + NavigatorID, + TabNavigationState, + TabNavigationOptions, + TabNavigationEventMap +> & + TabActionHelpers; + // The props accepted by the component is a combination of 3 things type Props = DefaultNavigatorOptions< ParamListBase, + string | undefined, TabNavigationState, TabNavigationOptions, - TabNavigationEventMap + TabNavigationEventMap, + TabNavigationProp > & TabRouterOptions & TabNavigationConfig; -function TabNavigator({ - id, - initialRouteName, - children, - layout, - screenListeners, - screenOptions, - screenLayout, - backBehavior, - tabBarStyle, - contentStyle, -}: Props) { +function TabNavigator({ tabBarStyle, contentStyle, ...rest }: Props) { const { state, navigation, descriptors, NavigationContent } = useNavigationBuilder< TabNavigationState, @@ -261,16 +288,7 @@ function TabNavigator({ TabActionHelpers, TabNavigationOptions, TabNavigationEventMap - >(TabRouter, { - id, - initialRouteName, - children, - layout, - screenListeners, - screenOptions, - screenLayout, - backBehavior, - }); + >(TabRouter, rest); return ( @@ -321,6 +339,7 @@ function TabNavigator({ ); } +// The factory function with generic types for type-inference export function createMyNavigator< const ParamList extends ParamListBase, const NavigatorID extends string | undefined = undefined, diff --git a/versioned_docs/version-7.x/deep-linking.md b/versioned_docs/version-7.x/deep-linking.md index 0ecb6de11c..6b6389861f 100755 --- a/versioned_docs/version-7.x/deep-linking.md +++ b/versioned_docs/version-7.x/deep-linking.md @@ -20,12 +20,12 @@ Below, we'll go through required configurations so that the deep link integratio ## Setup with Expo projects -First, you will want to specify a URL scheme for your app. This corresponds to the string before `://` in a URL, so if your scheme is `mychat` then a link to your app would be `mychat://`. You can register for a scheme in your `app.json` by adding a string under the scheme key: +First, you will want to specify a URL scheme for your app. This corresponds to the string before `://` in a URL, so if your scheme is `example` then a link to your app would be `example://`. You can register for a scheme in your `app.json` by adding a string under the scheme key: ```json { "expo": { - "scheme": "mychat" + "scheme": "example" } } ``` @@ -97,7 +97,7 @@ const linking = { ### Setup on iOS -Let's configure the native iOS app to open based on the `mychat://` URI scheme. +Let's configure the native iOS app to open based on the `example://` URI scheme. You'll need to link `RCTLinking` to your project by following the steps described here. To be able to listen to incoming app links, you'll need to add the following lines to `AppDelegate.m` in your project: @@ -132,12 +132,12 @@ Now you need to add the scheme to your project configuration. The easiest way to do this is with the `uri-scheme` package by running the following: ```bash -npx uri-scheme add mychat --ios +npx uri-scheme add example --ios ``` If you want to do it manually, open the project (e.g. `SimpleApp/ios/SimpleApp.xcworkspace`) in Xcode. Select the project in sidebar and navigate to the info tab. Scroll down to "URL Types" and add one. In the new URL type, set the identifier and the URL scheme to your desired URL scheme. -![Xcode project info URL types with mychat added](/assets/deep-linking/xcode-linking.png) +![Xcode project info URL types with example added](/assets/deep-linking/xcode-linking.png) To make sure Universal Links work in your app, you also need to setup [Associated Domains](https://developer.apple.com/documentation/Xcode/supporting-associated-domains) on your server. @@ -157,7 +157,7 @@ If you're using React Navigation within a hybrid app - an iOS app that has both To configure the external linking in Android, you can create a new intent in the manifest. -The easiest way to do this is with the `uri-scheme` package: `npx uri-scheme add mychat --android`. +The easiest way to do this is with the `uri-scheme` package: `npx uri-scheme add example --android`. If you want to add it manually, open up `SimpleApp/android/app/src/main/AndroidManifest.xml`, and make the following adjustments: @@ -176,7 +176,7 @@ If you want to add it manually, open up `SimpleApp/android/app/src/main/AndroidM - + ``` @@ -200,7 +200,7 @@ After adding them, it should look like this: - + @@ -246,7 +246,7 @@ npx uri-scheme open [your deep link] --[ios|android] For example: ```bash -npx uri-scheme open "mychat://chat/jane" --ios +npx uri-scheme open "example://chat/jane" --ios ``` Or if using Expo client: @@ -266,7 +266,7 @@ xcrun simctl openurl booted [your deep link] For example: ```bash -xcrun simctl openurl booted "mychat://chat/jane" +xcrun simctl openurl booted "example://chat/jane" ``` ### Testing with `adb` on Android @@ -280,7 +280,7 @@ adb shell am start -W -a android.intent.action.VIEW -d [your deep link] [your an For example: ```bash -adb shell am start -W -a android.intent.action.VIEW -d "mychat://chat/jane" com.simpleapp +adb shell am start -W -a android.intent.action.VIEW -d "example://chat/jane" com.simpleapp ``` Or if using Expo client: diff --git a/versioned_docs/version-7.x/drawer-layout.md b/versioned_docs/version-7.x/drawer-layout.md index 045b88280f..4cf3b5aca5 100644 --- a/versioned_docs/version-7.x/drawer-layout.md +++ b/versioned_docs/version-7.x/drawer-layout.md @@ -38,32 +38,7 @@ Then, you need to install and configure the libraries that are required by the d 2. Configure the Reanimated Babel Plugin in your project following the [installation guide](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started). -3. To finalize the installation of `react-native-gesture-handler`, we need to conditionally import it. To do this, create 2 files: - - ```js title="gesture-handler.native.js" - // Only import react-native-gesture-handler on native platforms - import 'react-native-gesture-handler'; - ``` - - ```js title="gesture-handler.js" - // Don't import react-native-gesture-handler on web - ``` - - Now, add the following at the **top** (make sure it's at the top and there's nothing else before it) of your entry file, such as `index.js` or `App.js`: - - ```js - import './gesture-handler'; - ``` - - Since the drawer layout doesn't use `react-native-gesture-handler` on Web, this avoids unnecessarily increasing the bundle size. - - :::warning - - If you are building for Android or iOS, do not skip this step, or your app may crash in production even if it works fine in development. This is not applicable to other platforms. - - ::: - -4. If you're on a Mac and developing for iOS, you also need to install the pods (via [Cocoapods](https://cocoapods.org/)) to complete the linking. +3. If you're on a Mac and developing for iOS, you also need to install the pods (via [Cocoapods](https://cocoapods.org/)) to complete the linking. ```bash npx pod-install ios diff --git a/versioned_docs/version-7.x/drawer-navigator.md b/versioned_docs/version-7.x/drawer-navigator.md index 78fd52c3e0..cdd6ff8f66 100644 --- a/versioned_docs/version-7.x/drawer-navigator.md +++ b/versioned_docs/version-7.x/drawer-navigator.md @@ -38,32 +38,7 @@ Then, you need to install and configure the libraries that are required by the d 2. Configure the Reanimated Babel Plugin in your project following the [installation guide](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started). -3. To finalize the installation of `react-native-gesture-handler`, we need to conditionally import it. To do this, create 2 files: - - ```js title="gesture-handler.native.js" - // Only import react-native-gesture-handler on native platforms - import 'react-native-gesture-handler'; - ``` - - ```js title="gesture-handler.js" - // Don't import react-native-gesture-handler on web - ``` - - Now, add the following at the **top** (make sure it's at the top and there's nothing else before it) of your entry file, such as `index.js` or `App.js`: - - ```js - import './gesture-handler'; - ``` - - Since the drawer navigator doesn't use `react-native-gesture-handler` on Web, this avoids unnecessarily increasing the bundle size. - - :::warning - - If you are building for Android or iOS, do not skip this step, or your app may crash in production even if it works fine in development. This is not applicable to other platforms. - - ::: - -4. If you're on a Mac and developing for iOS, you also need to install the pods (via [Cocoapods](https://cocoapods.org/)) to complete the linking. +3. If you're on a Mac and developing for iOS, you also need to install the pods (via [Cocoapods](https://cocoapods.org/)) to complete the linking. ```bash npx pod-install ios @@ -203,6 +178,7 @@ It supports the following values: - `initialRoute` - return to initial screen passed in `initialRouteName` prop, if not passed, defaults to the first screen - `order` - return to screen defined before the focused screen - `history` - return to last visited screen in the navigator; if the same screen is visited multiple times, the older entries are dropped from the history +- `fullHistory` - return to last visited screen in the navigator; doesn't drop duplicate entries unlike `history` - this behavior is useful to match how web pages work - `none` - do not handle back button #### `defaultStatus` diff --git a/versioned_docs/version-7.x/getting-started.md b/versioned_docs/version-7.x/getting-started.md index 9e747fe04b..d09c637cda 100755 --- a/versioned_docs/version-7.x/getting-started.md +++ b/versioned_docs/version-7.x/getting-started.md @@ -15,10 +15,10 @@ If you're already familiar with JavaScript, React and React Native, then you'll Here are some resources to help you out: -1. [React Native](https://reactnative.dev/docs/getting-started) -2. [Main Concepts of React](https://react.dev/learn) -3. [React Hooks](https://react.dev/reference/react) -4. [React Context](https://react.dev/learn/passing-data-deeply-with-context) (Advanced) +1. [Main Concepts of React](https://react.dev/learn) +2. [Getting started with React Native](https://reactnative.dev/docs/getting-started) +3. [React Hooks](https://react.dev/reference/react/hooks) +4. [React Context](https://react.dev/learn/passing-data-deeply-with-context) ## Minimum requirements @@ -147,32 +147,14 @@ There are 2 primary ways to configure the navigators: ### Static configuration -The static configuration API has reduced boilerplate and simplifies things such as TypeScript types and deep linking. If you're starting a new project or are new to React Navigation, this is the **recommended way** to set up your app. If you need more flexibility in the future, you can always mix and match with the dynamic configuration. +The static configuration API lets you write your configuration in an object, and is defined statically, i.e. it cannot change at runtime. This has reduced boilerplate and simplifies things such as TypeScript types and deep linking. + +If you're starting a new project or are new to React Navigation, this is the **recommended way** to set up your app. If you need more flexibility in the future, you can always mix and match with the dynamic configuration. Continue to ["Hello React Navigation"](hello-react-navigation.md?config=static) to start writing some code with the static API. ### Dynamic configuration -The dynamic configuration allows for more flexibility but requires more boilerplate and configuration (e.g. for deep links, typescript etc.). - -To get started with dynamic configuration, first, we need to wrap your app in `NavigationContainer`. Usually, you'd do this in your entry file, such as `index.js` or `App.js`: - -```js -import * as React from 'react'; -// highlight-next-line -import { NavigationContainer } from '@react-navigation/native'; - -export default function App() { - return ( - {/* Rest of your app code */} - ); -} -``` - -:::warning - -In a typical React Native app, the `NavigationContainer` should be only used once in your app at the root. You shouldn't nest multiple `NavigationContainer`s unless you have a specific use case for them. - -::: +The dynamic configuration API lets you write your configuration in React components, and can change at runtime based on state or props. This allows for more flexibility but requires more boilerplate and configuration for Typescript types, deep linking etc. Continue to ["Hello React Navigation"](hello-react-navigation.md?config=dynamic) to start writing some code with the dynamic API. diff --git a/versioned_docs/version-7.x/handling-safe-area.md b/versioned_docs/version-7.x/handling-safe-area.md index 99937132e4..225bfa87c1 100755 --- a/versioned_docs/version-7.x/handling-safe-area.md +++ b/versioned_docs/version-7.x/handling-safe-area.md @@ -473,6 +473,6 @@ Similarly, you could apply these paddings in `contentContainerStyle` of `FlatLis ## Summary -- Use `useSafeAreaInsets` hook from `react-native-safe-area-context` instead of `SafeAreaView` component +- Use [`useSafeAreaInsets`](https://appandflow.github.io/react-native-safe-area-context/api/use-safe-area-insets) hook from `react-native-safe-area-context` instead of [`SafeAreaView`](https://reactnative.dev/docs/safeareaview) component - Don't wrap your whole app in `SafeAreaView`, instead apply the styles to content inside your screens - Apply only specific insets using the `useSafeAreaInsets` hook for more control diff --git a/versioned_docs/version-7.x/header-buttons.md b/versioned_docs/version-7.x/header-buttons.md index 025baecf06..8861a6120b 100755 --- a/versioned_docs/version-7.x/header-buttons.md +++ b/versioned_docs/version-7.x/header-buttons.md @@ -297,6 +297,6 @@ Generally, this is what you want. But it's possible that in some circumstances t ## Summary -- You can set buttons in the header through the `headerLeft` and `headerRight` properties in `options`. -- The back button is fully customizable with `headerLeft`, but if you just want to change the title or image, there are other `options` for that — `headerBackTitle`, `headerBackTitleStyle`, and `headerBackImageSource`. -- You can use a callback for the options prop to access `navigation` and `route` objects. +- You can set buttons in the header through the [`headerLeft`](elements.md#headerleft) and [`headerRight`](elements.md#headerright) properties in [`options`](screen-options.md). +- The back button is fully customizable with `headerLeft`, but if you only want to change the title or image, there are other `options` for that — [`headerBackTitle`](native-stack-navigator.md#headerbacktitle), [`headerBackTitleStyle`](native-stack-navigator.md#headerbacktitlestyle), and [`headerBackImageSource`](native-stack-navigator.md#headerbackimagesource). +- You can use a callback for the options prop to access [`navigation`](navigation-object.md) and [`route`](route-object.md) objects. diff --git a/versioned_docs/version-7.x/headers.md b/versioned_docs/version-7.x/headers.md index 9bfd242ea5..04f1881160 100755 --- a/versioned_docs/version-7.x/headers.md +++ b/versioned_docs/version-7.x/headers.md @@ -689,6 +689,6 @@ You can read the full list of available `options` for screens inside of a native ## Summary -- You can customize the header inside of the `options` property of your screens. Read the full list of options [in the API reference](native-stack-navigator.md#options). -- The `options` property can be an object or a function. When it is a function, it is provided with an object with the `navigation` and `route` objects. -- You can also specify shared `screenOptions` in the stack navigator configuration when you initialize it. This will apply to all screens in the navigator. +- You can customize the header inside of the [`options`](screen-options.md) property of your screens. Read the full list of options [in the API reference](native-stack-navigator.md#options). +- The `options` property can be an object or a function. When it is a function, it is provided with an object with the [`navigation`](navigation-object.md) and [`route`](route-object.md) objects. +- You can also specify shared [`screenOptions`](screen-options.md#screenoptions-prop-on-the-navigator) in the stack navigator configuration when you initialize it. This will apply to all screens in the navigator. diff --git a/versioned_docs/version-7.x/hello-react-navigation.md b/versioned_docs/version-7.x/hello-react-navigation.md index 5f4bfeaf0d..74e4e98f60 100755 --- a/versioned_docs/version-7.x/hello-react-navigation.md +++ b/versioned_docs/version-7.x/hello-react-navigation.md @@ -42,7 +42,7 @@ npm install @react-navigation/elements `createNativeStackNavigator` is a function that takes a configuration object containing the screens and customization options. The screens are React Components that render the content displayed by the navigator. -`createStaticNavigation` is a function that takes the navigator defined earlier and returns a component that can be rendered in the app. It's only called once in the app. +`createStaticNavigation` is a function that takes the navigator defined earlier and returns a component that can be rendered in the app. It's only called once in the app. Usually, we'd render the returned component at the root of our app, which is usually the component exported from `App.js`, `App.tsx` etc., or used with `AppRegistry.registerComponent`, `Expo.registerRootComponent` etc. ```js name="Native Stack Example" snack // In App.js in a new project @@ -73,12 +73,18 @@ export default function App() { } ``` +:::warning + +In a typical React Native app, the `createStaticNavigation` function should be only used once in your app at the root. + +::: + `createNativeStackNavigator` is a function that returns an object containing 2 properties: `Screen` and `Navigator`. Both of them are React components used for configuring the navigator. The `Navigator` should contain `Screen` elements as its children to define the configuration for routes. -`NavigationContainer` is a component that manages our navigation tree and contains the [navigation state](navigation-state.md). This component must wrap all the navigators in the app. Usually, we'd render this component at the root of our app, which is usually the component exported from `App.js`. +`NavigationContainer` is a component that manages our navigation tree and contains the [navigation state](navigation-state.md). This component must wrap all the navigators in the app. Usually, we'd render this component at the root of our app, which is usually the component exported from `App.js`, `App.tsx` etc., or used with `AppRegistry.registerComponent`, `Expo.registerRootComponent` etc. ```js name="Native Stack Example" snack // In App.js in a new project @@ -115,6 +121,12 @@ export default function App() { } ``` +:::warning + +In a typical React Native app, the `NavigationContainer` should be only used once in your app at the root. You shouldn't nest multiple `NavigationContainer`s unless you have a specific use case for them. + +::: + @@ -525,19 +537,19 @@ If you are using TypeScript, you will need to specify the types accordingly. You - React Native doesn't have a built-in API for navigation like a web browser does. React Navigation provides this for you, along with the iOS and Android gestures and animations to transition between screens. -- `createNativeStackNavigator` is a function that takes the screens configuration and renders our content. +- [`createNativeStackNavigator`](native-stack-navigator.md) is a function that takes the screens configuration and renders our content. - Each property under screens refers to the name of the route, and the value is the component to render for the route. -- To specify what the initial route in a stack is, provide an `initialRouteName` option for the navigator. -- To specify screen-specific options, we can specify an `options` property, and for common options, we can specify `screenOptions`. +- To specify what the initial route in a stack is, provide an [`initialRouteName`](navigator.md#initial-route-name) option for the navigator. +- To specify screen-specific options, we can specify an [`options`](screen-options.md#options-prop-on-screen) property, and for common options, we can specify [`screenOptions`](screen-options.md#screenoptions-prop-on-the-navigator). - React Native doesn't have a built-in API for navigation like a web browser does. React Navigation provides this for you, along with the iOS and Android gestures and animations to transition between screens. -- `Stack.Navigator` is a component that takes route configuration as its children with additional props for configuration and renders our content. -- Each `Stack.Screen` component takes a `name` prop which refers to the name of the route and `component` prop which specifies the component to render for the route. These are the 2 required props. -- To specify what the initial route in a stack is, provide an `initialRouteName` as the prop for the navigator. -- To specify screen-specific options, we can pass an `options` prop to `Stack.Screen`, and for common options, we can pass `screenOptions` to `Stack.Navigator`. +- [`Stack.Navigator`](native-stack-navigator.md) is a component that takes route configuration as its children with additional props for configuration and renders our content. +- Each [`Stack.Screen`](screen.md) component takes a [`name`](screen.md#name) prop which refers to the name of the route and [`component`](screen.md#component) prop which specifies the component to render for the route. These are the 2 required props. +- To specify what the initial route in a stack is, provide an [`initialRouteName`](navigator.md#initial-route-name) as the prop for the navigator. +- To specify screen-specific options, we can pass an [`options`](screen-options.md#options-prop-on-screen) prop to `Stack.Screen`, and for common options, we can pass [`screenOptions`](screen-options.md#screenoptions-prop-on-the-navigator) to `Stack.Navigator`. diff --git a/versioned_docs/version-7.x/modal.md b/versioned_docs/version-7.x/modal.md index b55c05b78d..e9921b5d23 100755 --- a/versioned_docs/version-7.x/modal.md +++ b/versioned_docs/version-7.x/modal.md @@ -185,7 +185,7 @@ Instead of specifying this option for a group, it's also possible to specify it ## Summary -- To change the type of transition on a stack navigator you can use the `presentation` option. +- To change the type of transition on a stack navigator you can use the [`presentation`](native-stack-navigator.md#presentation) option. - When `presentation` is set to `modal`, the screens behave like a modal, i.e. they have a bottom to top transition and may show part of the previous screen in the background. - Setting `presentation: 'modal'` on a group makes all the screens in the group modals, so to use non-modal transitions on other screens, we add another group with the default configuration. diff --git a/versioned_docs/version-7.x/native-stack-navigator.md b/versioned_docs/version-7.x/native-stack-navigator.md index 8ec238a587..7c1fdc2086 100755 --- a/versioned_docs/version-7.x/native-stack-navigator.md +++ b/versioned_docs/version-7.x/native-stack-navigator.md @@ -174,7 +174,7 @@ This will have no effect on the first screen in the stack. #### `headerBackTitle` -Title string used by the back button on iOS. Defaults to the previous scene's title. +Title string used by the back button on iOS. Defaults to the previous scene's title, "Back" or arrow icon depending on the available space. See `headerBackButtonDisplayMode` to read about limitations and customize the behavior. Use `headerBackButtonDisplayMode: "minimal"` to hide it. @@ -195,7 +195,6 @@ Supported values: The space-aware behavior is disabled when: - The iOS version is 13 or lower -- Custom back title is set (e.g. with `headerBackTitle`) - Custom font family or size is set (e.g. with `headerBackTitleStyle`) - Back button menu is disabled (e.g. with `headerBackButtonMenuEnabled`) @@ -712,27 +711,43 @@ Only supported on Android and iOS. #### `statusBarStyle` -Sets the status bar color (similar to the `StatusBar` component). Defaults to `auto`. +Sets the status bar color (similar to the `StatusBar` component). Supported values: -- `"auto"` +- `"auto"` (iOS only) - `"inverted"` (iOS only) - `"dark"` - `"light"` +Defaults to `auto` on iOS and `light` on Android. + Requires setting `View controller-based status bar appearance -> YES` (or removing the config) in your `Info.plist` file. Only supported on Android and iOS. #### `statusBarBackgroundColor` +:::warning + +This option is deprecated and will be removed in a future release (for apps targeting Android SDK 35 or above edge-to-edge mode is enabled by default +and it is expected that the edge-to-edge will be enforced in future SDKs, see [here](https://developer.android.com/about/versions/15/behavior-changes-15#ux) for more information). + +::: + Sets the background color of the status bar (similar to the `StatusBar` component). Only supported on Android. #### `statusBarTranslucent` +:::warning + +This option is deprecated and will be removed in a future release (for apps targeting Android SDK 35 or above edge-to-edge mode is enabled by default +and it is expected that the edge-to-edge will be enforced in future SDKs, see [here](https://developer.android.com/about/versions/15/behavior-changes-15#ux) for more information). + +::: + Sets the translucency of the status bar (similar to the `StatusBar` component). Defaults to `false`. Only supported on Android. @@ -1261,6 +1276,13 @@ Only supported on iOS. #### `navigationBarColor` +:::warning + +This option is deprecated and will be removed in a future release (for apps targeting Android SDK 35 or above edge-to-edge mode is enabled by default +and it is expected that the edge-to-edge will be enforced in future SDKs, see [here](https://developer.android.com/about/versions/15/behavior-changes-15#ux) for more information). + +::: + Sets the navigation bar color. Defaults to initial status bar color. Only supported on Android. diff --git a/versioned_docs/version-7.x/navigating.md b/versioned_docs/version-7.x/navigating.md index 8babb1a7e9..73057ef519 100755 --- a/versioned_docs/version-7.x/navigating.md +++ b/versioned_docs/version-7.x/navigating.md @@ -358,8 +358,8 @@ export default function App() { ## Summary -- `navigation.navigate('RouteName')` pushes a new route to the native stack navigator if you're not already on that route. -- We can call `navigation.push('RouteName')` as many times as we like and it will continue pushing routes. -- The header bar will automatically show a back button, but you can programmatically go back by calling `navigation.goBack()`. On Android, the hardware back button just works as expected. -- You can go back to an existing screen in the stack with `navigation.popTo('RouteName')`, and you can go back to the first screen in the stack with `navigation.popToTop()`. -- The `navigation` object is available to all screen components with the [`useNavigation`](use-navigation.md) hook. +- [`navigation.navigate('RouteName')`](navigation-object.md#navigate) pushes a new route to the native stack navigator if you're not already on that route. +- We can call [`navigation.push('RouteName')`](stack-actions.md#push) as many times as we like and it will continue pushing routes. +- The header bar will automatically show a back button, but you can programmatically go back by calling [`navigation.goBack()`](navigation-object.md#goback). On Android, the hardware back button just works as expected. +- You can go back to an existing screen in the stack with [`navigation.popTo('RouteName')`](stack-actions.md#popto), and you can go back to the first screen in the stack with [`navigation.popToTop()`](stack-actions.md#poptotop). +- The [`navigation`](navigation-object.md) object is available to all screen components with the [`useNavigation`](use-navigation.md) hook. diff --git a/versioned_docs/version-7.x/navigation-actions.md b/versioned_docs/version-7.x/navigation-actions.md index 21f1c76250..91b6dfa41f 100755 --- a/versioned_docs/version-7.x/navigation-actions.md +++ b/versioned_docs/version-7.x/navigation-actions.md @@ -70,9 +70,6 @@ function HomeScreen() { > Navigate to Profile - ); } @@ -90,52 +87,6 @@ function ProfileScreen({ route }) { > Profile! {route.params.user}'s profile - - - - ); } @@ -192,9 +143,6 @@ function HomeScreen() { > Navigate to Profile - ); } @@ -213,52 +161,6 @@ function ProfileScreen({ route }) { > Profile! {route.params.user}'s profile - - - - ); } @@ -343,9 +245,6 @@ function HomeScreen() { > Navigate to Profile - ); } @@ -363,13 +262,6 @@ function ProfileScreen({ route }) { > Profile! {route.params.user}'s profile - - - ); } @@ -465,9 +336,6 @@ function HomeScreen() { > Navigate to Profile - ); } @@ -486,17 +354,6 @@ function ProfileScreen({ route }) { > Profile! {route.params.user}'s profile - - - ); } @@ -634,15 +470,6 @@ function HomeScreen() { > Navigate to Profile - ); } @@ -662,46 +489,9 @@ function ProfileScreen({ route }) { {route.params.user}'s profile - - - - ); } @@ -789,50 +570,9 @@ function ProfileScreen({ route }) { {route.params.user}'s profile - - - - ); } @@ -916,48 +653,13 @@ function ProfileScreen({ route }) { > Profile! {route.params.user}'s profile - - - - ); } @@ -1039,31 +738,6 @@ function ProfileScreen({ route }) { > Profile! {route.params.user}'s profile - - - ); } @@ -1409,53 +1080,14 @@ function ProfileScreen({ route }) { > Profile! {route.params.user}'s profile - - - ); @@ -1511,9 +1143,6 @@ function HomeScreen() { > Navigate to Profile - ); } @@ -1534,51 +1163,184 @@ function ProfileScreen({ route }) { {route.params.user}'s profile + + ); +} + +const Stack = createStackNavigator(); + +export default function App() { + return ( + + + + + + + ); +} +``` + + + + +If you want to replace params for a particular route, you can add a `source` property referring to the route key: + + + + +```js name="Common actions setParams" snack +import * as React from 'react'; +import { View, Text } from 'react-native'; +import { Button } from '@react-navigation/elements'; +import { + createStaticNavigation, + useNavigation, + CommonActions, +} from '@react-navigation/native'; +import { createStackNavigator } from '@react-navigation/stack'; + +function HomeScreen() { + const navigation = useNavigation(); + + return ( + + Home! + + ); +} + +function ProfileScreen({ route }) { + const navigation = useNavigation(); + return ( + + Profile! + {route.params.user}'s profile + + ); +} + +const Stack = createStackNavigator({ + screens: { + Home: HomeScreen, + Profile: ProfileScreen, + }, +}); + +const Navigation = createStaticNavigation(Stack); + +export default function App() { + return ; +} +``` + + + + +```js name="Common actions setParams" snack +import * as React from 'react'; +import { View, Text } from 'react-native'; +import { Button } from '@react-navigation/elements'; +import { + NavigationContainer, + CommonActions, + useNavigation, +} from '@react-navigation/native'; +import { createStackNavigator } from '@react-navigation/stack'; + +function HomeScreen() { + const navigation = useNavigation(); + + return ( + + Home! + + + ); +} + +function ProfileScreen({ route }) { + const navigation = useNavigation(); + + return ( + + Profile! + {route.params.user}'s profile ); @@ -1601,12 +1363,18 @@ export default function App() { -If you want to set params for a particular route, you can add a `source` property referring to the route key: +If the `source` property is explicitly set to `undefined`, it'll replace the params for the focused route. + +### replaceParams + +The `replaceParams` action allows to replace params for a certain route. It takes the following arguments: + +- `params` - _object_ - required - New params to use for the route. -```js name="Common actions setParams" snack +```js name="Common actions replaceParams" snack import * as React from 'react'; import { View, Text } from 'react-native'; import { Button } from '@react-navigation/elements'; @@ -1639,9 +1407,6 @@ function HomeScreen() { > Navigate to Profile - ); } @@ -1659,53 +1424,14 @@ function ProfileScreen({ route }) { > Profile! {route.params.user}'s profile - - - ); @@ -1728,7 +1454,7 @@ export default function App() { -```js name="Common actions setParams" snack +```js name="Common actions replaceParams" snack import * as React from 'react'; import { View, Text } from 'react-native'; import { Button } from '@react-navigation/elements'; @@ -1761,9 +1487,6 @@ function HomeScreen() { > Navigate to Profile - ); } @@ -1784,51 +1507,187 @@ function ProfileScreen({ route }) { {route.params.user}'s profile + + ); +} + +const Stack = createStackNavigator(); + +export default function App() { + return ( + + + + + + + ); +} +``` + + + + +If you want to replace params for a particular route, you can add a `source` property referring to the route key: + + + + +```js name="Common actions replaceParams" snack +import * as React from 'react'; +import { View, Text } from 'react-native'; +import { Button } from '@react-navigation/elements'; +import { + createStaticNavigation, + useNavigation, + CommonActions, +} from '@react-navigation/native'; +import { createStackNavigator } from '@react-navigation/stack'; + +function HomeScreen() { + const navigation = useNavigation(); + + return ( + + Home! + + + ); +} + +function ProfileScreen({ route }) { + const navigation = useNavigation(); + return ( + + Profile! + {route.params.user}'s profile + + ); +} + +const Stack = createStackNavigator({ + screens: { + Home: HomeScreen, + Profile: ProfileScreen, + }, +}); + +const Navigation = createStaticNavigation(Stack); + +export default function App() { + return ; +} +``` + + + + +```js name="Common actions replaceParams" snack +import * as React from 'react'; +import { View, Text } from 'react-native'; +import { Button } from '@react-navigation/elements'; +import { + NavigationContainer, + CommonActions, + useNavigation, +} from '@react-navigation/native'; +import { createStackNavigator } from '@react-navigation/stack'; + +function HomeScreen() { + const navigation = useNavigation(); + + return ( + + Home! + + + ); +} + +function ProfileScreen({ route }) { + const navigation = useNavigation(); + + return ( + + Profile! + {route.params.user}'s profile ); @@ -1851,4 +1710,4 @@ export default function App() { -If the `source` property is explicitly set to `undefined`, it'll set the params for the focused route. +If the `source` property is explicitly set to `undefined`, it'll replace the params for the focused route. diff --git a/versioned_docs/version-7.x/navigation-container.md b/versioned_docs/version-7.x/navigation-container.md index 27c46df6ee..7fed8c5242 100644 --- a/versioned_docs/version-7.x/navigation-container.md +++ b/versioned_docs/version-7.x/navigation-container.md @@ -463,7 +463,7 @@ const Navigation = createStaticNavigation(RootStack); function App() { const linking = { - prefixes: ['https://mychat.com/', 'mychat://'], + prefixes: ['https://example.com/', 'example://'], }; return ( @@ -484,7 +484,7 @@ import { NavigationContainer } from '@react-navigation/native'; function App() { const linking = { - prefixes: ['https://mychat.com/', 'mychat://'], + prefixes: ['https://example.com/', 'example://'], config: { screens: { Home: 'feed/:sort', @@ -525,7 +525,7 @@ Example: Loading...} /> @@ -538,7 +538,7 @@ Example: listener(url); @@ -701,7 +701,7 @@ import messaging from '@react-native-firebase/messaging'; -```js name="Navigate - replace and reset" snack +```js name="Navigation object replace and reset" snack import * as React from 'react'; import { Button } from '@react-navigation/elements'; import { View, Text } from 'react-native'; @@ -590,7 +590,7 @@ export default App; -```js name="Navigate - replace and reset" snack +```js name="Navigation object replace and reset" snack import * as React from 'react'; import { Button } from '@react-navigation/elements'; import { View, Text } from 'react-native'; @@ -972,7 +972,7 @@ The `setParams` method lets us update the params (`route.params`) of the current -```js name="Navigate - setParams" snack +```js name="Navigation object setParams" snack import * as React from 'react'; import { Button } from '@react-navigation/elements'; import { View, Text } from 'react-native'; @@ -1074,7 +1074,7 @@ export default App; -```js name="Navigate - setParams" snack +```js name="Navigation object setParams" snack import * as React from 'react'; import { Button } from '@react-navigation/elements'; import { View, Text } from 'react-native'; @@ -1173,6 +1173,214 @@ export default App; +### `replaceParams` + +The `replaceParams` method lets us replace the params (`route.params`) of the current screen with a new params object. + + + + +```js name="Navigation object replaceParams" snack +import * as React from 'react'; +import { Button } from '@react-navigation/elements'; +import { View, Text } from 'react-native'; +import { + useNavigation, + createStaticNavigation, +} from '@react-navigation/native'; +import { createNativeStackNavigator } from '@react-navigation/native-stack'; + +function HomeScreen() { + const navigation = useNavigation(); + + return ( + + This is the home screen of the app + + + ); +} + +// codeblock-focus-start +function ProfileScreen({ route }) { + const navigation = useNavigation(); + + return ( + + Profile Screen + Friends: + {route.params.friends[0]} + {route.params.friends[1]} + {route.params.friends[2]} + + + + ); +} +// codeblock-focus-end + +const Stack = createNativeStackNavigator({ + initialRouteName: 'Home', + screens: { + Home: HomeScreen, + Profile: { + screen: ProfileScreen, + options: ({ route }) => ({ title: route.params.title }), + }, + }, +}); + +const Navigation = createStaticNavigation(Stack); + +function App() { + return ; +} + +export default App; +``` + + + + +```js name="Navigation object replaceParams" snack +import * as React from 'react'; +import { Button } from '@react-navigation/elements'; +import { View, Text } from 'react-native'; +import { NavigationContainer, useNavigation } from '@react-navigation/native'; +import { createNativeStackNavigator } from '@react-navigation/native-stack'; + +function HomeScreen() { + const navigation = useNavigation(); + + return ( + + This is the home screen of the app + + + ); +} + +// codeblock-focus-start +function ProfileScreen({ route }) { + const navigation = useNavigation(); + + return ( + + Profile Screen + Friends: + {route.params.friends[0]} + {route.params.friends[1]} + {route.params.friends[2]} + + + + ); +} +// codeblock-focus-end + +const Stack = createNativeStackNavigator(); + +function App() { + return ( + + + + ({ title: route.params.title })} + /> + + + ); +} + +export default App; +``` + + + + ### `setOptions` The `setOptions` method lets us set screen options from within the component. This is useful if we need to use the component's props, state or context to configure our screen. @@ -1180,7 +1388,7 @@ The `setOptions` method lets us set screen options from within the component. Th -```js name="Navigate - setOptions" snack +```js name="Navigation object setOptions" snack import * as React from 'react'; import { View, Text, TextInput } from 'react-native'; import { Button } from '@react-navigation/elements'; @@ -1270,7 +1478,7 @@ export default App; -```js name="Navigate - setOptions" snack +```js name="Navigation object setOptions" snack import * as React from 'react'; import { View, Text, TextInput } from 'react-native'; import { Button } from '@react-navigation/elements'; diff --git a/versioned_docs/version-7.x/navigation-state.md b/versioned_docs/version-7.x/navigation-state.md index 46d2bf13a2..43d2a59a7b 100644 --- a/versioned_docs/version-7.x/navigation-state.md +++ b/versioned_docs/version-7.x/navigation-state.md @@ -30,14 +30,14 @@ There are few properties present in every navigation state object: - `routes` - List of route objects (screens) which are rendered in the navigator. It also represents the history in a stack navigator. There should be at least one item present in this array. - `index` - Index of the focused route object in the `routes` array. - `history` - A list of visited items. This is an optional property and not present in all navigators. For example, it's only present in tab and drawer navigators in the core. The shape of the items in the `history` array can vary depending on the navigator. There should be at least one item present in this array. -- `stale` - A navigation state is assumed to be stale unless the `stale` property is explicitly set to `false`. This means that the state object needs to be ["rehydrated"](#partial-state-objects). +- `stale` - A navigation state is assumed to be stale unless the `stale` property is explicitly set to `false`. This means that the state object needs to be ["rehydrated"](#stale-state-objects). Each route object in a `routes` array may contain the following properties: - `key` - Unique key of the screen. Created automatically or added while navigating to this screen. - `name` - Name of the screen. Defined in navigator component hierarchy. - `params` - An optional object containing params which is defined while navigating e.g. `navigate('Home', { sortBy: 'latest' })`. -- `state` - An optional object containing the navigation state of a child navigator nested inside this screen. +- `state` - An optional object containing the [stale navigation state](#stale-state-objects) of a child navigator nested inside this screen. For example, a stack navigator containing a tab navigator nested inside it's home screen may have a navigation state object like this: @@ -69,11 +69,17 @@ const state = { It's important to note that even if there's a nested navigator, the `state` property on the `route` object is not added until a navigation happens, hence it's not guaranteed to exist. -## Partial state objects +## Stale state objects -Earlier there was a mention of `stale` property in the navigation state. A stale navigation state means that the state object needs to be rehydrated or fixed or fixed up, such as adding missing keys, removing invalid screens etc. before being used. As a user, you don't need to worry about it, React Navigation will fix up any issues in a state object automatically unless `stale` is set to `false`. If you're writing a [custom router](custom-routers.md), the `getRehydratedState` method let's you write custom rehydration logic to fix up state objects. +Earlier there was a mention of `stale` property in the navigation state. If the `stale` property is set to `true` or is missing, the state is assumed to be stale. A stale navigation state means that the state object may be partial, such as missing keys or routes, contain invalid routes, or may not be up-to-date. A stale state can be a result of [deep linking](deep-linking.md), r[estoring from a persisted state](state-persistence.md) etc. -This also applies to the `index` property: `index` should be the last route in a stack, and if a different value was specified, React Navigation fixes it. For example, if you wanted to reset your app's navigation state to have it display the `Profile` route, and have the `Home` route displayed upon going back, and did the below, +If you're accessing the navigation state of a navigator using the built-in APIs such as [`useNavigationState()`](use-navigation-state.md), [`navigation.getState()`](navigation-object.md#getstate) etc., the state object is guaranteed to be complete and not stale. However, if you try to access a child navigator's state with the `state` property on the `route` object, it maybe a stale or partial state object. So it's not recommended to use this property directly. + +Using the [`ref.getRootState()`](navigation-container.md#getrootstate) API will always return a complete and up-to-date state object for the current navigation tree, including any nested child navigators. + +When React Navigation encounters stale or partial state, it will automatically fix it up before using it. This includes adding missing keys, removing any invalid routes, ensuring the `index` is correct etc. This process of fixing stale state is called **rehydration**. If you're writing a [custom router](custom-routers.md), the `getRehydratedState` method lets you write custom rehydration logic to fix up state objects. + +For example, `index` should be the last route in a stack, and if a different value was specified, React Navigation fixes it. For example, if you wanted to reset your app's navigation state to have it display the `Profile` route, and have the `Home` route displayed upon going back, and dispatched the following action: ```js navigation.reset({ @@ -82,7 +88,7 @@ navigation.reset({ }); ``` -React Navigation would correct `index` to 1, and display the route and perform navigation as intended. +React Navigation would correct `index` to `1` before the routes are displayed. This feature comes handy when doing operations such as [reset](navigation-actions.md#reset), [providing a initial state](navigation-container.md#initialstate) etc., as you can safely omit many properties from the navigation state object and relying on React Navigation to add those properties for you, making your code simpler. For example, you can only provide a `routes` array without any keys and React Navigation will automatically add everything that's needed to make it work: @@ -118,4 +124,4 @@ If you want React Navigation to fix invalid state, you need to make sure that yo ::: -When you're providing a state object in [`initialState`](navigation-container.md#initialstate), React Navigation will always assume that it's a stale state object, which makes sure that things like state persistence work smoothly without extra manipulation of the state object. +When you're providing a state object in [`initialState`](navigation-container.md#initialstate), React Navigation will always assume that it's a stale state object, since navigation configuration may have changed since the last time. This makes sure that things like [state persistence](state-persistence.md) work smoothly without extra manipulation of the state object. diff --git a/versioned_docs/version-7.x/params.md b/versioned_docs/version-7.x/params.md index 47124f64d5..4905bd4ef5 100755 --- a/versioned_docs/version-7.x/params.md +++ b/versioned_docs/version-7.x/params.md @@ -193,9 +193,11 @@ export default function App() { } ``` +The `setParams` method merges the new params with the existing ones. To replace the existing params, you can use [`replaceParams`](navigation-object.md#replaceparams) instead. + :::note -Avoid using `setParams` to update screen options such as `title` etc. If you need to update options, use [`setOptions`](navigation-object.md#setoptions) instead. +Avoid using `setParams` or `replaceParams` to update screen options such as `title` etc. If you need to update options, use [`setOptions`](navigation-object.md#setoptions) instead. ::: @@ -371,6 +373,17 @@ export default function App() { See [Nesting navigators](nesting-navigators.md) for more details on nesting. +## Reserved param names + +Some param names are reserved by React Navigation as part of the API for nested navigators. The list of the reserved param names are as follows: + +- `screen` +- `params` +- `initial` +- `state` + +You should avoid using these param names in your code unless navigating to a screen containing a nested navigator. Otherwise it will result in unexpected behavior, such as the screen not being able to access the params you passed. If you need to pass data to a nested screen, use a different names for the param. + ## What should be in params Params are essentially options for a screen. They should contain the minimal data required to show a screen, nothing more. If the data is used by multiple screens, it should be in a global store or global cache. Params is not designed for state management. @@ -421,8 +434,9 @@ In essence, pass the least amount of data required to identify a screen in param ## Summary -- `navigate` and `push` accept an optional second argument to let you pass parameters to the route you are navigating to. For example: `navigation.navigate('RouteName', { paramName: 'value' })`. -- You can read the params through `route.params` inside a screen -- You can update the screen's params with `navigation.setParams` -- Initial params can be passed via the `initialParams` prop on `Screen` +- [`navigate`](navigation-actions.md#navigate) and [`push`](stack-actions.md#push) accept an optional second argument to let you pass parameters to the route you are navigating to. For example: `navigation.navigate('RouteName', { paramName: 'value' })`. +- You can read the params through [`route.params`](route-object.md) inside a screen +- You can update the screen's params with [`navigation.setParams`](navigation-object.md#setparams) or [`navigation.replaceParams`](navigation-object.md#replaceparams) +- Initial params can be passed via the [`initialParams`](screen.md#initial-params) prop on `Screen` or in the navigator config - Params should contain the minimal data required to show a screen, nothing more +- Some [param names are reserved](#reserved-param-names) by React Navigation and should be avoided diff --git a/versioned_docs/version-7.x/server-rendering.md b/versioned_docs/version-7.x/server-rendering.md index 0c388ffcb3..4dddb2019b 100644 --- a/versioned_docs/version-7.x/server-rendering.md +++ b/versioned_docs/version-7.x/server-rendering.md @@ -12,7 +12,7 @@ This guide will cover how to server render your React Native app using React Nat 1. Rendering the correct layout depending on the request URL 2. Setting appropriate page metadata based on the focused screen -::: warning +:::warning Server rendering support is currently limited. It's not possible to provide a seamless SSR experience due to a lack of APIs such as media queries. In addition, many third-party libraries often don't work well with server rendering. diff --git a/versioned_docs/version-7.x/stack-navigator.md b/versioned_docs/version-7.x/stack-navigator.md index 803fb376ea..1255563111 100755 --- a/versioned_docs/version-7.x/stack-navigator.md +++ b/versioned_docs/version-7.x/stack-navigator.md @@ -42,32 +42,7 @@ Then, you need to install and configure the libraries that are required by the s npm install react-native-gesture-handler ``` -2. To finalize the installation of `react-native-gesture-handler`, we need to conditionally import it. To do this, create 2 files: - - ```js title="gesture-handler.native.js" - // Only import react-native-gesture-handler on native platforms - import 'react-native-gesture-handler'; - ``` - - ```js title="gesture-handler.js" - // Don't import react-native-gesture-handler on web - ``` - - Now, add the following at the **top** (make sure it's at the top and there's nothing else before it) of your entry file, such as `index.js` or `App.js`: - - ```js - import './gesture-handler'; - ``` - - Since the stack navigator doesn't use `react-native-gesture-handler` on Web, this avoids unnecessarily increasing the bundle size. - - :::warning - - If you are building for Android or iOS, do not skip this step, or your app may crash in production even if it works fine in development. This is not applicable to other platforms. - - ::: - -3. Optionally, you can also install [`@react-native-masked-view/masked-view`](https://github.com/react-native-masked-view/masked-view). This is needed if you want to use UIKit style animations for the header ([`HeaderStyleInterpolators.forUIKit`](#headerstyleinterpolators)). +2. Optionally, you can also install [`@react-native-masked-view/masked-view`](https://github.com/react-native-masked-view/masked-view). This is needed if you want to use UIKit style animations for the header ([`HeaderStyleInterpolators.forUIKit`](#headerstyleinterpolators)). If you have a Expo managed project, in your project directory, run: @@ -81,7 +56,7 @@ Then, you need to install and configure the libraries that are required by the s npm install @react-native-masked-view/masked-view ``` -4. If you're on a Mac and developing for iOS, you also need to install the pods (via [Cocoapods](https://cocoapods.org/)) to complete the linking. +3. If you're on a Mac and developing for iOS, you also need to install the pods (via [Cocoapods](https://cocoapods.org/)) to complete the linking. ```bash npx pod-install ios diff --git a/versioned_docs/version-7.x/state-persistence.md b/versioned_docs/version-7.x/state-persistence.md index bc69a8ec6f..1e9cc94e24 100755 --- a/versioned_docs/version-7.x/state-persistence.md +++ b/versioned_docs/version-7.x/state-persistence.md @@ -77,6 +77,27 @@ const SettingsStackScreen = createNativeStackNavigator({ }, }); +const Tab = createBottomTabNavigator({ + screens: { + Home: { + screen: HomeStackScreen, + options: { + headerShown: false, + tabBarLabel: 'Home!', + }, + }, + Settings: { + screen: SettingsStackScreen, + options: { + headerShown: false, + tabBarLabel: 'Settings!', + }, + }, + }, +}); + +const Navigation = createStaticNavigation(Tab); + // codeblock-focus-start const PERSISTENCE_KEY = 'NAVIGATION_STATE_V1'; @@ -111,25 +132,6 @@ export default function App() { if (!isReady) { return null; } - const Tab = createBottomTabNavigator({ - screens: { - Home: { - screen: HomeStackScreen, - options: { - headerShown: false, - tabBarLabel: 'Home!', - }, - }, - Settings: { - screen: SettingsStackScreen, - options: { - headerShown: false, - tabBarLabel: 'Settings!', - }, - }, - }, - }); - const Navigation = createStaticNavigation(Tab); return ( + + + + ); +} + // codeblock-focus-start const PERSISTENCE_KEY = 'NAVIGATION_STATE_V1'; @@ -252,18 +271,7 @@ export default function App() { AsyncStorage.setItem(PERSISTENCE_KEY, JSON.stringify(state)) } > - - - - + ); } @@ -303,4 +311,4 @@ if (!isReady) { Each param, route, and navigation state must be fully serializable for this feature to work. Typically, you would serialize the state as a JSON string. This means that your routes and params must contain no functions, class instances, or recursive data structures. React Navigation already [warns you during development](troubleshooting.md#i-get-the-warning-non-serializable-values-were-found-in-the-navigation-state) if it encounters non-serializable data, so watch out for the warning if you plan to persist navigation state. -You can modify the initial state object before passing it to container, but note that if your `initialState` isn't a [valid navigation state](navigation-state.md#partial-state-objects), React Navigation may not be able to handle the situation gracefully. +You can modify the initial state object before passing it to container, but note that if your `initialState` isn't a [valid navigation state](navigation-state.md#stale-state-objects), React Navigation may not be able to handle the situation gracefully in some scenarios. diff --git a/versioned_docs/version-7.x/testing.md b/versioned_docs/version-7.x/testing.md index 6d3f1466f7..a39c33b1fd 100644 --- a/versioned_docs/version-7.x/testing.md +++ b/versioned_docs/version-7.x/testing.md @@ -18,7 +18,26 @@ When writing tests, it's encouraged to write tests that closely resemble how use Following these principles will help you write tests that are more reliable and easier to maintain by avoiding testing implementation details. -## Mocking native dependencies +## Setting up Jest + +### Compiling React Navigation + +React Navigation ships [ES modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules). However, Jest does not support ES modules natively. + +It's necessary to transform the code to CommonJS to use them in tests. The `react-native` preset for Jest does not transform the code in `node_modules` by default. To enable this, you need to add the [`transformIgnorePatterns`](https://jestjs.io/docs/configuration#transformignorepatterns-arraystring) option in your Jest configuration where you can specify a regexp pattern. To compile React Navigation packages, you can add `@react-navigation` to the regexp. + +This is usually done in a `jest.config.js` file or the `jest` key in `package.json`: + +```diff lang=json +{ + "preset": "react-native", ++ "transformIgnorePatterns": [ ++ "node_modules/(?!(@react-native|react-native|@react-navigation)/)" ++ ] +} +``` + +### Mocking native dependencies To be able to test React Navigation components, certain dependencies will need to be mocked depending on which components are being used. @@ -48,16 +67,19 @@ import { jest } from '@jest/globals'; jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper'); ``` -Then we need to use this setup file in our jest config. You can add it under `setupFilesAfterEnv` option in a `jest.config.js` file or the `jest` key in `package.json`: +Then we need to use this setup file in our jest config. You can add it under [`setupFilesAfterEnv`](https://jestjs.io/docs/configuration#setupfilesafterenv-array) option in a `jest.config.js` file or the `jest` key in `package.json`: -```json +```diff lang=json { "preset": "react-native", - "setupFilesAfterEnv": ["/jest/setup.js"] + "transformIgnorePatterns": [ + "node_modules/(?!(@react-native|react-native|@react-navigation)/)" + ], ++ "setupFilesAfterEnv": ["/jest/setup.js"] } ``` -Make sure that the path to the file in `setupFilesAfterEnv` is correct. Jest will run these files before running your tests, so it's the best place to put your global mocks. +Jest will run the files specified in `setupFilesAfterEnv` before running your tests, so it's a good place to put your global mocks.
Mocking `react-native-screens` @@ -761,3 +783,175 @@ In the above test, we: In a production app, we recommend using a library like [React Query](https://tanstack.com/query/) to handle data fetching and caching. The above example is for demonstration purposes only. ::: + +### Re-usable components + +To make it easier to test components that don't depend on the navigation structure, we can create a light-weight test navigator: + +```js title="TestStackNavigator.js" +import { useNavigationBuilder, StackRouter } from '@react-navigation/native'; + +function TestStackNavigator(props) { + const { state, descriptors, NavigationContent } = useNavigationBuilder( + StackRouter, + props + ); + + return ( + + {state.routes.map((route, index) => { + return ( + + {descriptors[route.key].render()} + + ); + })} + + ); +} + +export function createTestStackNavigator(config) { + return createNavigatorFactory(TestStackNavigator)(config); +} +``` + +This lets us test React Navigation specific logic such as `useFocusEffect` without needing to set up a full navigator. + +We can use this test navigator in our tests like this: + + + + +```js title="MyComponent.test.js" +import { act, render, screen } from '@testing-library/react-native'; +import { createStaticNavigation } from '@react-navigation/native'; +import { createTestStackNavigator } from './TestStackNavigator'; +import { MyComponent } from './MyComponent'; + +test('does not show modal when not focused', () => { + const TestStack = createTestStackNavigator({ + screens: { + A: MyComponent, + B: () => null, + }, + }); + + const Navigation = createStaticNavigation(TestStack); + + render( + + ); + + expect(screen.queryByText('Modal')).not.toBeVisible(); +}); + +test('shows modal when focused', () => { + const TestStack = createTestStackNavigator({ + screens: { + A: MyComponent, + B: () => null, + }, + }); + + const Navigation = createStaticNavigation(TestStack); + + render( + + ); + + expect(screen.getByText('Modal')).toBeVisible(); +}); +``` + + + + +```js title="MyComponent.test.js" +import { act, render, screen } from '@testing-library/react-native'; +import { NavigationContainer } from '@react-navigation/native'; +import { createTestStackNavigator } from './TestStackNavigator'; +import { MyComponent } from './MyComponent'; + +test('does not show modal when not focused', () => { + const Stack = createTestStackNavigator(); + + const TestStack = () => ( + + + null} /> + + ); + + render( + + + + ); + + expect(screen.queryByText('Modal')).not.toBeVisible(); +}); + +test('shows modal when focused', () => { + const Stack = createTestStackNavigator(); + + const TestStack = () => ( + + + null} /> + + ); + + render( + + + + ); + + expect(screen.getByText('Modal')).toBeVisible(); +}); +``` + + + + +Here we create a test stack navigator using the `createTestStackNavigator` function. We then render the `MyComponent` component within the test navigator and assert that the modal is shown or hidden based on the focus state. + +The `initialState` prop is used to set the initial state of the navigator, i.e. which screens are rendered in the stack and which one is focused. See [navigation state](navigation-state.md) for more information on the structure of the state object. + +You can also pass a [`ref`](navigation-container.md#ref) to programmatically navigate in your tests. + +The test navigator is a simplified version of the stack navigator, but it's still a real navigator and behaves like one. This means that you can use it to test any other navigation logic. + +See [Custom navigators](custom-navigators.md) for more information on how to write custom navigators if you want adjust the behavior of the test navigator or add more functionality. + +## Best practices + +Generally, we recommend avoiding mocking React Navigation. Mocking can help you isolate the component you're testing, but when testing components with navigation logic, mocking means that your tests don't test for the navigation logic. + +- Mocking APIs such as `useFocusEffect` means you're not testing the focus logic in your component. +- Mocking `navigation` prop or `useNavigation` means that the `navigation` object may not have the same shape as the real one. +- Asserting `navigation.navigate` calls means you only test that the function was called, not that the call was correct based on the navigation structure. +- etc. + +Avoiding mocks means additional work when writing tests, but it also means: + +- Refactors that don't change the logic won't break the tests, e.g. changing `navigation` prop to `useNavigation`, using a different navigation action that does the same thing, etc. +- Library upgrades or refactor that actually change the behavior will correctly break the tests, surfacing actual regressions. + +Tests should break when there's a regression, not due to a refactor. Otherwise it leads to additional work to fix the tests, making it harder to know when a regression is introduced. diff --git a/versioned_docs/version-7.x/typescript.md b/versioned_docs/version-7.x/typescript.md index 77755c0a14..3c51d8101b 100755 --- a/versioned_docs/version-7.x/typescript.md +++ b/versioned_docs/version-7.x/typescript.md @@ -9,11 +9,10 @@ import TabItem from '@theme/TabItem'; React Navigation can be configured to type-check screens and their params, as well as various other APIs using TypeScript. This provides better intelliSense and type safety when working with React Navigation. -:::note - -React Navigation is designed to work with [`strict`](https://www.typescriptlang.org/tsconfig/#strict) mode in TypeScript. If you are not using `strict` mode, some things might not work as expected. +First, make sure you have the following configuration in your `tsconfig.json` under `compilerOptions`: -::: +- `strict: true` or `strictNullChecks: true` - Necessary for intelliSense and type inference to work correctly. +- `moduleResolution: "bundler"` - Necessary to resolve the types correctly and match the behavior of [Metro](https://metrobundler.dev/) and other bundlers. @@ -70,9 +69,11 @@ There are 2 steps to configure TypeScript with the static API: ## Navigator specific types -Generally, we recommend using the default types for the [`useNavigation`](use-navigation.md) prop to access the navigation object in a navigator-agnostic manner. However, if you need to use navigator-specific APIs, you need to manually annotate [`useNavigation`](use-navigation.md): +Generally, we recommend using the default types for the [`useNavigation`](use-navigation.md) prop to access the navigation object in a navigator-agnostic manner. However, if you need to use navigator-specific APIs, e.g. `setOptions` to update navigator options, `push`, `pop`, `popTo` etc. with stacks, `openDrawer`, `closeDrawer` etc. with drawer and so on, you need to manually annotate [`useNavigation`](use-navigation.md): ```ts +import type { BottomTabNavigationProp } from '@react-navigation/bottom-tabs'; + type BottomTabParamList = StaticParamList; type ProfileScreenNavigationProp = BottomTabNavigationProp< BottomTabParamList, @@ -84,7 +85,13 @@ type ProfileScreenNavigationProp = BottomTabNavigationProp< const navigation = useNavigation(); ``` -Note that annotating [`useNavigation`](use-navigation.md) this way is not type-safe since we can't guarantee that the type you provided matches the type of the navigator. +Similarly, you can import `NativeStackNavigationProp` from [`@react-navigation/native-stack`](native-stack-navigator.md), `StackNavigationProp` from [`@react-navigation/stack`](stack-navigator.md), `DrawerNavigationProp` from [`@react-navigation/drawer`](drawer-navigator.md) etc. + +:::danger + +Annotating [`useNavigation`](use-navigation.md) this way is not type-safe since we can't guarantee that the type you provided matches the type of the navigator. So try to keep manual annotations to a minimum and use the default types instead. + +::: ## Nesting navigator using dynamic API @@ -229,7 +236,7 @@ If you have an [`id`](./navigator.md#id) prop for your navigator, you can do: type Props = NativeStackScreenProps; ``` -This allows us to type check route names and params which you're navigating using [`navigate`](navigation-object.md#navigate), [`push`](stack-actions.md#push) etc. The name of the current route is necessary to type check the params in `route.params` and when you call [`setParams`](navigation-actions#setparams). +This allows us to type check route names and params which you're navigating using [`navigate`](navigation-object.md#navigate), [`push`](stack-actions.md#push) etc. The name of the current route is necessary to type check the params in `route.params` and when you call [`setParams`](navigation-actions#setparams) or [`replaceParams`](navigation-actions#replaceparams). Similarly, you can import `StackScreenProps` from [`@react-navigation/stack`](stack-navigator.md), `DrawerScreenProps` from [`@react-navigation/drawer`](drawer-navigator.md), `BottomTabScreenProps` from [`@react-navigation/bottom-tabs`](bottom-tab-navigator.md) and so on. diff --git a/versioned_docs/version-7.x/upgrading-from-6.x.md b/versioned_docs/version-7.x/upgrading-from-6.x.md index 20822dc78a..fd5c971ced 100755 --- a/versioned_docs/version-7.x/upgrading-from-6.x.md +++ b/versioned_docs/version-7.x/upgrading-from-6.x.md @@ -336,7 +336,7 @@ The `popToTopOnBlur` option provides an alternative approach - it pops the scree See [Bottom Tab Navigator](bottom-tab-navigator.md#poptotoponblur) and [Drawer Navigator](drawer-navigator.md#poptotoponblur) docs for usage. -It's still possible to achieve the old behavior of `unmountOnBlur` by using the useIsFocused hook in the screen: +It's still possible to achieve the old behavior of `unmountOnBlur` by using the [`useIsFocused`](use-is-focused.md) hook in the screen: ```js const isFocused = useIsFocused(); @@ -346,7 +346,45 @@ if (!isFocused) { } ``` -This could also be combined with the new [layout props](#new-layout-props) to specify it at the screen configuration level. +This could also be combined with the new [layout props](#new-layout-props) to specify it at the screen configuration level to make the migration easier. + +To achieve this, define a component that uses the `useIsFocused` hook to conditionally render its children: + +```js +function UnmountOnBlur({ children }) { + const isFocused = useIsFocused(); + + if (!isFocused) { + return null; + } + + return children; +} +``` + +Then use the component in `layout` prop of the screen: + +```diff lang=js + {children}} + options={{ +- unmountOnBlur: true, + }} +/> +``` + +Or `screenLayout` prop of the navigator: + +```diff lang=js + {children}} + screenOptions={{ +- unmountOnBlur: true, + }} +> +``` #### The `tabBarTestID` option is renamed to `tabBarButtonTestID` in Bottom Tab Navigator and Material Top Tab Navigator @@ -494,17 +532,36 @@ Previously, the UI elements in React Navigation such as the header on platforms #### React Native Tab View now has a new API to specify various options -The API for the `TabView` and `TabBar` component in `react-native-tab-view` has been revamped. Previously, the `TabBar` took the following props: +The API for the `TabView` and `TabBar` component in `react-native-tab-view` has been revamped. + +Some of props accepted by the `TabBar` have now been replaced with `commonOptions` and `options` props on `TabView`: -- `getLabelText` -- `getAccessible` -- `getAccessibilityLabel` -- `getTestID` -- `renderIcon` -- `renderLabel` -- `renderBadge` +- `getLabelText` -> `labelText` +- `getAccessible` -> `accessible` +- `getAccessibilityLabel` -> `accessibilityLabel` +- `getTestID` -> `testID` +- `renderIcon` -> `icon` +- `renderLabel` -> `label` +- `renderBadge` -> `badge` +- `labelStyle` +- `sceneContainerStyle` -> `sceneStyle` + +To keep the same behavior when updating your existing code, move these props to `commonOptions` prop on `TabView`: + +```diff lang=js + ( +- ++ + )} ++ commonOptions={{ ++ label: renderLabel, ++ labelStyle, ++ }} +/> +``` -These props have been replaced with `commonOptions` and `options` props on `TabView`: +The options can also be customized individually for each tab by passing an object to the `options` prop with the `route.key` as the key and the options as the value: ```js