From 107c0c1a1f089b3223dc5eec4f7f34099325901c Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Fri, 9 Aug 2024 14:22:12 +0200 Subject: [PATCH 1/9] Document navigateDeprecated --- .../version-7.x/navigation-object.md | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/versioned_docs/version-7.x/navigation-object.md b/versioned_docs/version-7.x/navigation-object.md index a2c804e4db..3cea5259c5 100755 --- a/versioned_docs/version-7.x/navigation-object.md +++ b/versioned_docs/version-7.x/navigation-object.md @@ -244,7 +244,8 @@ In a stack navigator ([stack](stack-navigator.md) or [native stack](native-stack - If you're already on a screen with the same name, it will update its params and not push a new screen. - If you're on a different screen, it will push the new screen onto the stack. -- If the [`getId`](screen.md#getid) prop is specified, and another screen in the stack has the same ID, it will navigate to that screen and update its params instead. +- If the [`getId`](screen.md#getid) prop is specified, and another screen in the stack has the same ID, it will bring that screen to focus and update its params instead. +- If none of the above conditions match, it'll push a new screen to the stack. By default, the screen is identified by its name. But you can also customize it to take the params into account by using the [`getId`](screen.md#getid) prop. @@ -277,8 +278,11 @@ const Tabs = createBottomTabNavigator({ + Now, if you have a stack with the history `Home > Profile (userId: bob) > Settings` and you call `navigate(Profile, { userId: 'alice' })`, the resulting screens will be `Home > Profile (userId: bob) > Settings > Profile (userId: alice)` since it'll add a new `Profile` screen as no matching screen was found. +In a tab or drawer navigator, calling `navigate` will switch to the relevant screen if it's not focused already and update the params of the screen. + ### `navigateDeprecated` :::warning @@ -287,7 +291,23 @@ This method is deprecated and will be removed in a future release. It only exist ::: -TODO +The `navigateDeprecated` action implements the old behavior of `navigate` from previous versions. + +It takes the following arguments: + +`navigation.navigateDeprecated(name, params)` + +- `name` - A destination name of the route that has been defined somewhere +- `params` - Params to pass to the destination route. + +In a stack navigator ([stack](stack-navigator.md) or [native stack](native-stack-navigator.md)), calling `navigate` with a screen name will have the following behavior: + +- If you're already on a screen with the same name, it will update its params and not push a new screen. +- If a screen with the same name already exists in the stack, it will pop all the screens after it to go back to the existing screen. +- If the [`getId`](screen.md#getid) prop is specified, and another screen in the stack has the same ID, it will pop any screens to navigate to that screen and update its params instead. +- If none of the above conditions match, it'll push a new screen to the stack. + +In a tab or drawer navigator, calling `navigate` will switch to the relevant screen if it's not focused already and update the params of the screen. ### `goBack` From db2a8e845c9a20029327312e320bb931a743c6bb Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Fri, 9 Aug 2024 16:58:10 +0200 Subject: [PATCH 2/9] Remove redundant tab and drawer navigation guides --- .../examples/7.x/drawer-based-navigation.js | 36 --- .../version-7.x/bottom-tab-navigator.md | 8 +- .../custom-android-back-button-handling.md | 2 - ...vigation.md => customizing-bottom-tabs.md} | 248 ++---------------- .../version-7.x/drawer-navigator.md | 6 - versioned_docs/version-7.x/next-steps.md | 8 +- versioned_docs/version-7.x/status-bar.md | 2 - versioned_sidebars/version-7.x-sidebars.json | 3 +- 8 files changed, 23 insertions(+), 290 deletions(-) delete mode 100755 static/examples/7.x/drawer-based-navigation.js rename versioned_docs/version-7.x/{tab-based-navigation.md => customizing-bottom-tabs.md} (50%) diff --git a/static/examples/7.x/drawer-based-navigation.js b/static/examples/7.x/drawer-based-navigation.js deleted file mode 100755 index 967f5daa4c..0000000000 --- a/static/examples/7.x/drawer-based-navigation.js +++ /dev/null @@ -1,36 +0,0 @@ -import * as React from 'react'; -import { Button, View } from 'react-native'; -import { createDrawerNavigator } from '@react-navigation/drawer'; -import { NavigationContainer } from '@react-navigation/native'; - -function HomeScreen({ navigation }) { - return ( - - - - ); -} - -function SettingsScreen() { - const navigation = useNavigation(); - - return ( - - Settings! - - - ); -} -``` - - - -## A stack navigator for each tab - -Often tabs don't just display one screen — for example, on your Twitter feed, you can tap on a tweet and it brings you to a new screen within that tab with all of the replies. You can think of this as there being separate navigation stacks within each tab, and that's exactly how we will model it in React Navigation. - - +You can also update the badge from within the screen component by using the `setOptions` method: ```js -import * as React from 'react'; -import { Text, View } from 'react-native'; -import { NavigationContainer, useNavigation } from '@react-navigation/native'; -import { createNativeStackNavigator } from '@react-navigation/native-stack'; -import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; -import { Button } from '@react-navigation/elements'; - -function DetailsScreen() { - return ( - - Details! - - ); -} - -function HomeScreen() { - const navigation = useNavigation(); - - return ( - - Home screen - - - ); -} - -function SettingsScreen() { - const navigation = useNavigation(); - - return ( - - Settings screen - - - ); -} - -const HomeStack = createNativeStackNavigator(); - -function HomeStackScreen() { - return ( - - - - - ); -} - -const SettingsStack = createNativeStackNavigator(); +const navigation = useNavigation(); -function SettingsStackScreen() { - return ( - - - - - ); -} - -const Tab = createBottomTabNavigator(); - -export default function App() { - return ( - - - - - - - ); -} +React.useEffect(() => { + navigation.setOptions({ + tabBarBadge: unreadMessagesCount, + }); +}, [navigation, unreadMessagesCount]); ``` - - -## Why do we need a TabNavigator instead of TabBarIOS or some other component? - -It's common to attempt to use a standalone tab bar component without integrating it into the navigation library you use in your app. In some cases, this works fine! You should be warned, however, that you may run into some frustrating unanticipated issues when doing this. - -For example, React Navigation's tab navigator takes care of handling the Android back button for you, while standalone components typically do not. Additionally, it is more difficult for you (as the developer) to perform actions such as "jump to this tab and then go to this screen" if you need to call into two distinct APIs for it. Lastly, mobile user interfaces have numerous small design details that require that certain components are aware of the layout or presence of other components — for example, if you have a translucent tab bar, content should scroll underneath it and the scroll view should have an inset on the bottom equal to the height of the tab bar so you can see all of the content. Double tapping the tab bar should make the active navigation stack pop to the top of the stack, and doing it again should scroll the active scroll view in that stack scroll to the top. While not all of these behaviors are implemented out of the box yet with React Navigation, they will be and you will not get any of this if you use a standalone tab view component. - -## A tab navigator contains a stack and you want to hide the tab bar on specific screens - -[See the documentation here](hiding-tabbar-in-screens.md) +![Tabs with badges](/assets/navigators/tabs/tabs-badges.png) diff --git a/versioned_docs/version-7.x/drawer-navigator.md b/versioned_docs/version-7.x/drawer-navigator.md index cd9292daa4..6f265cb0c2 100644 --- a/versioned_docs/version-7.x/drawer-navigator.md +++ b/versioned_docs/version-7.x/drawer-navigator.md @@ -187,12 +187,6 @@ export default function App() { -:::note - -For a complete usage guide see [Drawer Navigation](drawer-based-navigation.md). - -::: - ## API Definition ### Props diff --git a/versioned_docs/version-7.x/next-steps.md b/versioned_docs/version-7.x/next-steps.md index d20aacd5c3..6efc2fb171 100755 --- a/versioned_docs/version-7.x/next-steps.md +++ b/versioned_docs/version-7.x/next-steps.md @@ -4,14 +4,14 @@ title: Next steps sidebar_label: Next steps --- -You are now familiar with how to create a stack navigator, configure it on your screen components, navigate between routes, and display modals. Stack navigators and their related APIs will be the most frequently used tools in your React Navigation toolbelt, but there are problems that they don't solve. For example, you can't build tab-based navigation using a stack navigator — for that, you need to use a [Bottom Tabs Navigator](bottom-tab-navigator.md). +You are now familiar with how to create a stack navigator, configure it on your screen components, navigate between routes, and display [modals](modal.md). Stack navigators and their related APIs will be the most frequently used tools in your React Navigation toolbelt, but there are problems that they don't solve. For example, you can't build tab-based navigation using a stack navigator — for that, you need to use a [Bottom Tabs Navigator](bottom-tab-navigator.md). -The rest of the documentation is organized around specific use cases, so you can jump between the sections under "Guides" as the need arises (but it also wouldn't hurt you to familiarize yourself with them pre-emptively!). +You can check out the **"Navigators"** section in the sidebar to learn more about the different navigators available in React Navigation. + +The rest of the documentation is organized around specific use cases, so you can jump between the sections under **"Guides"** as the need arises (but it also wouldn't hurt you to familiarize yourself with them pre-emptively!). Some of the guides you may want to check out are: -- [Tab navigation](tab-based-navigation.md): How to show tabs at the bottom of the screen. -- [Drawer navigation](drawer-based-navigation.md): How to show a drawer from the left or right side of the screen. - [Authentication flows](auth-flow.md): How to handle authentication flows in your app. - [Supporting safe areas](handling-safe-area.md): How to handle safe areas such as statusbar in your app. - [Deep linking](deep-linking.md): How to handle deep linking and universal links in your app. diff --git a/versioned_docs/version-7.x/status-bar.md b/versioned_docs/version-7.x/status-bar.md index 109ad679ac..9b97b4820d 100755 --- a/versioned_docs/version-7.x/status-bar.md +++ b/versioned_docs/version-7.x/status-bar.md @@ -230,8 +230,6 @@ function FocusAwareStatusBar(props) { Now, our screens (both `Screen1.js` and `Screen2.js`) will use the `FocusAwareStatusBar` component instead of the `StatusBar` component from React Native: - - diff --git a/versioned_sidebars/version-7.x-sidebars.json b/versioned_sidebars/version-7.x-sidebars.json index 0206c5480e..d15b206605 100644 --- a/versioned_sidebars/version-7.x-sidebars.json +++ b/versioned_sidebars/version-7.x-sidebars.json @@ -12,10 +12,9 @@ "next-steps" ], "Guides": [ - "tab-based-navigation", - "drawer-based-navigation", "auth-flow", "handling-safe-area", + "customizing-tabbar", "hiding-tabbar-in-screens", "status-bar", "modal", From ee9ab9ec4af2576339f644b8d16647fbf8ce6a83 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Fri, 9 Aug 2024 17:44:14 +0200 Subject: [PATCH 3/9] Add ability specify tabs in codeblock metadata --- docusaurus.config.js | 5 +++- src/components/Pre.js | 66 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/docusaurus.config.js b/docusaurus.config.js index 3a12db1ff9..46050882e0 100755 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -128,7 +128,10 @@ export default { sidebarCollapsed: false, remarkPlugins: [[remarkNpm2Yarn, { sync: true }]], rehypePlugins: [ - [rehypeCodeblockMeta, { match: { snack: true, lang: true } }], + [ + rehypeCodeblockMeta, + { match: { snack: true, lang: true, tabs: true } }, + ], ], }, blog: { diff --git a/src/components/Pre.js b/src/components/Pre.js index 777e3eadaa..227c15c070 100644 --- a/src/components/Pre.js +++ b/src/components/Pre.js @@ -3,12 +3,22 @@ import { useColorMode } from '@docusaurus/theme-common'; import { usePluginData } from '@docusaurus/useGlobalData'; import MDXPre from '@theme-original/MDXComponents/Pre'; import React from 'react'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +const SUPPORTED_TABS = { + config: [ + { value: 'static', label: 'Static', default: true }, + { value: 'dynamic', label: 'Dynamic' }, + ], +}; export default function Pre({ children, 'data-name': name, 'data-snack': snack, 'data-dependencies': deps, + 'data-tabs': tabs, 'data-lang': lang, ...rest }) { @@ -18,6 +28,62 @@ export default function Pre({ const child = React.Children.only(children); + // If we encounter tabs, we need to render 2 code blocks + if (tabs && tabs in SUPPORTED_TABS) { + return ( + + {SUPPORTED_TABS[tabs].map((tab) => { + const code = child.props.children; + + if (typeof code !== 'string') { + throw new Error( + 'Code to display in tabs must be a string, but received ' + + typeof code + ); + } + + const lines = code.split('\n'); + + let content = ''; + let exclude = false; + let indent; + + for (const line of lines) { + if (line.trim().startsWith('// codeblock-tabs=')) { + exclude = line.trim() !== `// codeblock-tabs=${tab.value}`; + } else if (line.trim() === '// codeblock-tabs-end') { + exclude = false; + } else if (!exclude) { + content += line + '\n'; + } + } + + return ( + +
+                {React.cloneElement(children, {
+                  ...child.props,
+                  children: content.trim(),
+                })}
+              
+
+ ); + })} +
+ ); + } + // Handle diffs with language if (child.props.className === 'language-diff' && lang) { const code = child.props.children; From 5c18f45083eec19b4f987b6d29bde3531361239d Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Fri, 9 Aug 2024 17:47:46 +0200 Subject: [PATCH 4/9] Update custom tab bar example --- .../version-7.x/bottom-tab-navigator.md | 90 ++++++++++++++++--- versioned_docs/version-7.x/params.md | 2 - 2 files changed, 79 insertions(+), 13 deletions(-) diff --git a/versioned_docs/version-7.x/bottom-tab-navigator.md b/versioned_docs/version-7.x/bottom-tab-navigator.md index 5914dcbe05..c56a9353ca 100755 --- a/versioned_docs/version-7.x/bottom-tab-navigator.md +++ b/versioned_docs/version-7.x/bottom-tab-navigator.md @@ -166,14 +166,27 @@ Style object for the component wrapping the screen content. Function that returns a React element to display as the tab bar. -Example: +The function receives an object containing the following properties as the argument: - +- `state` - The state object for the tab navigator. +- `descriptors` - The descriptors object containing options for the tab navigator. +- `navigation` - The navigation object for the tab navigator. -```js +The `state.routes` array contains all the routes defined in the navigator. Each route's options can be accessed using `descriptors[route.key].options`. + +Example: + +```js name="Custom tab bar" snack tabs=config +import * as React from 'react'; +import { + createStaticNavigation, + NavigationContainer, +} from '@react-navigation/native'; // codeblock-focus-start -import { View, Text, TouchableOpacity, Platform } from 'react-native'; +import { View, Platform } from 'react-native'; import { useLinkBuilder, useTheme } from '@react-navigation/native'; +import { Text, PlatformPressable } from '@react-navigation/elements'; +import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; function MyTabBar({ state, descriptors, navigation }) { const { colors } = useTheme(); @@ -212,9 +225,8 @@ function MyTabBar({ state, descriptors, navigation }) { }; return ( - {label} - + ); })} ); } + // codeblock-focus-end -// ... +function HomeScreen() { + return ( + + Home Screen + + ); +} + +function ProfileScreen() { + return ( + + Profile Screen + + ); +} - }> - {/* ... */} -; +// codeblock-tabs=static +// codeblock-focus-start +const MyTabs = createBottomTabNavigator({ + // highlight-next-line + tabBar: (props) => , + screens: { + Home: HomeScreen, + Profile: ProfileScreen, + }, +}); +// codeblock-focus-end + +const Navigation = createStaticNavigation(MyTabs); + +export default function App() { + return ; +} +// codeblock-tabs-end + +// codeblock-tabs=dynamic +const Tab = createBottomTabNavigator(); + +// codeblock-focus-start +function MyTabs() { + return ( + } + > + + + + ); +} +// codeblock-focus-end + +export default function App() { + return ( + + + + ); +} +// codeblock-tabs-end ``` This example will render a basic tab bar with labels. diff --git a/versioned_docs/version-7.x/params.md b/versioned_docs/version-7.x/params.md index 9f447c584b..c5c5b26d1c 100755 --- a/versioned_docs/version-7.x/params.md +++ b/versioned_docs/version-7.x/params.md @@ -125,8 +125,6 @@ You can also pass some initial params to a screen. If you didn't specify any par
- - ```js Date: Sat, 10 Aug 2024 00:13:38 +0200 Subject: [PATCH 5/9] Move footer to homepage only --- docusaurus.config.js | 3 --- src/pages/home/Footer.js | 30 ++++++++++++++++++++++++++++++ src/pages/index.js | 11 +++++------ 3 files changed, 35 insertions(+), 9 deletions(-) create mode 100644 src/pages/home/Footer.js diff --git a/docusaurus.config.js b/docusaurus.config.js index 46050882e0..2ad89381c8 100755 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -97,9 +97,6 @@ export default { }, ], }, - footer: { - copyright: `Copyright © ${new Date().getFullYear()} React Navigation. Built with Docusaurus, GitHub Pages, and Netlify.`, - }, }, plugins: [ './src/plugins/react-navigation-versions.mjs', diff --git a/src/pages/home/Footer.js b/src/pages/home/Footer.js new file mode 100644 index 0000000000..133a2bb6de --- /dev/null +++ b/src/pages/home/Footer.js @@ -0,0 +1,30 @@ +import React from 'react'; + +const links = [ + { title: 'Docusaurus', href: 'https://docusaurus.io/' }, + { title: 'GitHub Pages', href: 'https://pages.github.com/' }, + { title: 'Netlify', href: 'https://www.netlify.com/' }, +]; + +export default function Footer() { + return ( +
+
+ +
+
+ ); +} diff --git a/src/pages/index.js b/src/pages/index.js index b47413293a..9ad7899c42 100755 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -1,12 +1,10 @@ -import React from 'react'; -import Layout from '@theme/Layout'; -import Link from '@docusaurus/Link'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import useBaseUrl from '@docusaurus/useBaseUrl'; -import sponsors from '../data/sponsors'; +import Layout from '@theme/Layout'; +import React from 'react'; -import Splash from './home/Splash'; import Features from './home/Features'; +import Footer from './home/Footer'; +import Splash from './home/Splash'; import Sponsors from './home/Sponsors'; const features = [ @@ -52,6 +50,7 @@ function Home() { +