diff --git a/versioned_docs/version-7.x/use-focus-effect.md b/versioned_docs/version-7.x/use-focus-effect.md index e7494fa394b..b6c7a5b1527 100755 --- a/versioned_docs/version-7.x/use-focus-effect.md +++ b/versioned_docs/version-7.x/use-focus-effect.md @@ -4,30 +4,105 @@ title: useFocusEffect sidebar_label: useFocusEffect --- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + Sometimes we want to run side-effects when a screen is focused. A side effect may involve things like adding an event listener, fetching data, updating document title, etc. While this can be achieved using `focus` and `blur` events, it's not very ergonomic. To make this easier, the library exports a `useFocusEffect` hook: - + + -```js +```js name="useFocusEffect hook" snack version=7 +import * as React from 'react'; +import { View } from 'react-native'; +import { createStaticNavigation } from '@react-navigation/native'; +import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; +// codeblock-focus-start import { useFocusEffect } from '@react-navigation/native'; -function Profile({ userId }) { - const [user, setUser] = React.useState(null); - +function ProfileScreen() { useFocusEffect( React.useCallback(() => { - const unsubscribe = API.subscribe(userId, user => setUser(user)); + // Do something when the screen is focused + return () => { + // Do something when the screen is unfocused + // Useful for cleanup functions + }; + }, []) + ); - return () => unsubscribe(); - }, [userId]) + return ; +} +// codeblock-focus-end + +function HomeScreen() { + return ; +} + +const Tab = createBottomTabNavigator({ + screens: { + Home: HomeScreen, + Profile: ProfileScreen, + }, +}); + +const Navigation = createStaticNavigation(Tab); + +export default function App() { + return ; +} +``` + + + + +```js name="useFocusEffect hook" snack version=7 +import * as React from 'react'; +import { View } from 'react-native'; +import { NavigationContainer } from '@react-navigation/native'; +import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; + +// codeblock-focus-start +import { useFocusEffect } from '@react-navigation/native'; + +function ProfileScreen() { + useFocusEffect( + React.useCallback(() => { + // Do something when the screen is focused + return () => { + // Do something when the screen is unfocused + // Useful for cleanup functions + }; + }, []) ); - return ; + return ; +} +// codeblock-focus-end + +function HomeScreen() { + return ; +} + +const Tab = createBottomTabNavigator(); + +export default function App() { + return ( + + + + + + + ); } ``` + + + :::warning To avoid the running the effect too often, it's important to wrap the callback in `useCallback` before passing it to `useFocusEffect` as shown in the example. @@ -107,7 +182,7 @@ useFocusEffect( React.useCallback(() => { return () => { // Do something that should run on blur - } + }; }, []) ); ``` @@ -116,7 +191,7 @@ The cleanup function runs whenever the effect needs to cleanup, i.e. on `blur`, ```js React.useEffect(() => { - const unsubscribe = navigation.addListener('blur', () => { + const unsubscribe = navigation.addListener('blur', () => { // Do something when the screen blurs }); @@ -146,7 +221,7 @@ function FetchUserData({ userId, onUpdate }) { // ... class Profile extends React.Component { - _handleUpdate = user => { + _handleUpdate = (user) => { // Do something with user object }; diff --git a/versioned_docs/version-7.x/use-is-focused.md b/versioned_docs/version-7.x/use-is-focused.md index 69ab01fdc82..73e03e44b06 100755 --- a/versioned_docs/version-7.x/use-is-focused.md +++ b/versioned_docs/version-7.x/use-is-focused.md @@ -4,22 +4,98 @@ title: useIsFocused sidebar_label: useIsFocused --- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + We might want to render different content based on the current focus state of the screen. The library exports a `useIsFocused` hook to make this easier: - + + -```js +```js name="useIsFocused hook" snack version=7 +import * as React from 'react'; +import { View, Text } from 'react-native'; +import { createStaticNavigation } from '@react-navigation/native'; +import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs'; +// codeblock-focus-start import { useIsFocused } from '@react-navigation/native'; -// ... +function ProfileScreen() { + // This hook returns `true` if the screen is focused, `false` otherwise + // highlight-next-line + const isFocused = useIsFocused(); + + return ( + + {isFocused ? 'focused' : 'unfocused'} + + ); +} +// codeblock-focus-end -function Profile() { +function HomeScreen() { + return ; +} + +const Tab = createMaterialTopTabNavigator({ + screens: { + Home: HomeScreen, + Profile: ProfileScreen, + }, +}); + +const Navigation = createStaticNavigation(Tab); + +export default function App() { + return ; +} +``` + + + + +```js name="useIsFocused hook" snack version=7 +import * as React from 'react'; +import { View, Text } from 'react-native'; +import { NavigationContainer } from '@react-navigation/native'; +import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs'; +// codeblock-focus-start +import { useIsFocused } from '@react-navigation/native'; + +function ProfileScreen() { + // This hook returns `true` if the screen is focused, `false` otherwise + // highlight-next-line const isFocused = useIsFocused(); - return {isFocused ? 'focused' : 'unfocused'}; + return ( + + {isFocused ? 'focused' : 'unfocused'} + + ); +} +// codeblock-focus-end + +function HomeScreen() { + return ; +} + +const Tab = createMaterialTopTabNavigator(); + +export default function App() { + return ( + + + + + + + ); } ``` + + + Note that using this hook triggers a re-render for the component when the screen it's in changes focus. This might cause lags during the animation if your component is heavy. You might want to extract the expensive parts to separate components and use [`React.memo`](https://reactjs.org/docs/react-api.html#reactmemo) or [`React.PureComponent`](https://reactjs.org/docs/react-api.html#reactpurecomponent) to minimize re-renders for them. ## Using with class component @@ -35,7 +111,7 @@ class Profile extends React.Component { } // Wrap and export -export default function(props) { +export default function (props) { const isFocused = useIsFocused(); return ; diff --git a/versioned_docs/version-7.x/use-navigation-state.md b/versioned_docs/version-7.x/use-navigation-state.md index 59bdc9b67f3..fa66274eb3f 100755 --- a/versioned_docs/version-7.x/use-navigation-state.md +++ b/versioned_docs/version-7.x/use-navigation-state.md @@ -4,6 +4,9 @@ title: useNavigationState sidebar_label: useNavigationState --- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + `useNavigationState` is a hook which gives access to the [navigation state](navigation-state.md) of the navigator which contains the screen. It's useful in rare cases where you want to render something based on the navigation state. :::warning @@ -15,13 +18,13 @@ Consider the navigator's state object to be internal and subject to change in a It takes a selector function as an argument. The selector will receive the full [navigation state](navigation-state.md) and can return a specific value from the state: ```js -const index = useNavigationState(state => state.index); +const index = useNavigationState((state) => state.index); ``` The selector function helps to reduce unnecessary re-renders, so your screen will re-render only when that's something you care about. If you actually need the whole state object, you can do this explicitly: ```js -const state = useNavigationState(state => state); +const state = useNavigationState((state) => state); ``` :::warning @@ -44,16 +47,201 @@ function Profile() { In this example, even if you push a new screen, this text won't update. If you use the hook, it'll work as expected: - + + -```js -function Profile() { - const routesLength = useNavigationState(state => state.routes.length); +```js name="useNavigation hook" snack version=7 +import * as React from 'react'; +import Button from '@react-navigation/elements'; +import { View, Text } from 'react-native'; +import { + createStaticNavigation, + useNavigation, + useRoute, +} from '@react-navigation/native'; +import { createNativeStackNavigator } from '@react-navigation/native-stack'; +// codeblock-focus-start +import { useNavigationState } from '@react-navigation/native'; - return Number of routes: {routesLength}; +function useIsFirstRouteInParent() { + const route = useRoute(); + const isFirstRouteInParent = useNavigationState( + (state) => state.routes[0].key === route.key + ); + + return isFirstRouteInParent; +} + +function usePreviousRouteName() { + return useNavigationState((state) => + state.routes[state.index - 1]?.name + ? state.routes[state.index - 1].name + : 'None' + ); +} +// codeblock-focus-end + +function HomeScreen() { + const navigation = useNavigation(); + const isFirstRoute = useIsFirstRouteInParent(); + const previousRouteName = usePreviousRouteName(); + return ( + + It is {isFirstRoute ? '' : 'not '}first route in navigator + Previous route name: {previousRouteName} + + + + ); +} + +function ProfileScreen() { + const navigation = useNavigation(); + const isFirstRoute = useIsFirstRouteInParent(); + const previousRouteName = usePreviousRouteName(); + return ( + + It is {isFirstRoute ? '' : 'not '}first route in navigator + Previous route name: {previousRouteName} + + + + ); +} + +function SettingsScreen() { + const navigation = useNavigation(); + const isFirstRoute = useIsFirstRouteInParent(); + const previousRouteName = usePreviousRouteName(); + return ( + + It is {isFirstRoute ? '' : 'not '}first route in navigator + Previous route name: {previousRouteName} + + + ); +} + +const Stack = createNativeStackNavigator({ + screens: { + Home: HomeScreen, + Profile: ProfileScreen, + Settings: SettingsScreen, + }, +}); + +const Navigation = createStaticNavigation(Stack); + +export default function App() { + return ; +} +``` + + + + +```js name="useNavigationState hook" snack version=7 +import * as React from 'react'; +import Button from '@react-navigation/elements'; +import { View, Text } from 'react-native'; +import { + NavigationContainer, + useRoute, + useNavigation, +} from '@react-navigation/native'; +import { createNativeStackNavigator } from '@react-navigation/native-stack'; + +// codeblock-focus-start +import { useNavigationState } from '@react-navigation/native'; + +function useIsFirstRouteInParent() { + const route = useRoute(); + const isFirstRouteInParent = useNavigationState( + (state) => state.routes[0].key === route.key + ); + + return isFirstRouteInParent; +} + +function usePreviousRouteName() { + return useNavigationState((state) => + state.routes[state.index - 1]?.name + ? state.routes[state.index - 1].name + : 'None' + ); +} +// codeblock-focus-end + +function HomeScreen({ navigation }) { + const isFirstRoute = useIsFirstRouteInParent(); + const previousRouteName = usePreviousRouteName(); + return ( + + It is {isFirstRoute ? '' : 'not '}first route in navigator + Previous route name: {previousRouteName} + + + + ); +} + +function ProfileScreen({ navigation }) { + const isFirstRoute = useIsFirstRouteInParent(); + const previousRouteName = usePreviousRouteName(); + return ( + + It is {isFirstRoute ? '' : 'not '}first route in navigator + Previous route name: {previousRouteName} + + + + ); +} + +function SettingsScreen({ navigation }) { + const isFirstRoute = useIsFirstRouteInParent(); + const previousRouteName = usePreviousRouteName(); + return ( + + It is {isFirstRoute ? '' : 'not '}first route in navigator + Previous route name: {previousRouteName} + + + ); +} + +const Stack = createNativeStackNavigator(); + +function MyStack() { + return ( + + + + + + ); +} + +export default function App() { + return ( + + + + ); } ``` + + + So when do you use `navigation.getState()`? It's mostly useful within event listeners where you don't care about what's rendered. In most cases, using the hook should be preferred. ## Using with class component @@ -69,8 +257,8 @@ class Profile extends React.Component { } // Wrap and export -export default function(props) { - const routesLength = useNavigationState(state => state.routes.length); +export default function (props) { + const routesLength = useNavigationState((state) => state.routes.length); return ; } diff --git a/versioned_docs/version-7.x/use-navigation.md b/versioned_docs/version-7.x/use-navigation.md index 07d0cfff595..7c994cc0a2f 100755 --- a/versioned_docs/version-7.x/use-navigation.md +++ b/versioned_docs/version-7.x/use-navigation.md @@ -4,31 +4,145 @@ title: useNavigation sidebar_label: useNavigation --- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + `useNavigation` is a hook that gives access to `navigation` object. It's useful when you cannot pass the `navigation` object as a prop to the component directly, or don't want to pass it in case of a deeply nested child. The `useNavigation` hook returns the `navigation` object of the screen where it's used: - + + -```js +```js name="useNavigation hook" snack version=7 +import * as React from 'react'; +import { View, Text } from 'react-native'; +import { Button } from '@react-navigation/elements'; +import { createStaticNavigation } from '@react-navigation/native'; +import { createNativeStackNavigator } from '@react-navigation/native-stack'; +// codeblock-focus-start +import { useNavigation } from '@react-navigation/native'; + +function MyBackButton() { + // highlight-next-line + const navigation = useNavigation(); + + return ( + + ); +} +// codeblock-focus-end + +function HomeScreen() { + const navigation = useNavigation(); + + return ( + + This is the home screen of the app + + + ); +} + +function ProfileScreen() { + return ( + + Profile Screen + + + ); +} + +const Stack = createNativeStackNavigator({ + initialRouteName: 'Home', + screens: { + Home: HomeScreen, + Profile: ProfileScreen, + }, +}); + +const Navigation = createStaticNavigation(Stack); + +function App() { + return ; +} + +export default App; +``` + + + + +```js name="useNavigation hook" snack version=7 import * as React from 'react'; -import { Button } from 'react-native'; +import { View, Text } from 'react-native'; +import { Button } from '@react-navigation/elements'; +import { NavigationContainer } from '@react-navigation/native'; +import { createNativeStackNavigator } from '@react-navigation/native-stack'; +// codeblock-focus-start import { useNavigation } from '@react-navigation/native'; function MyBackButton() { + // highlight-next-line const navigation = useNavigation(); return ( + ); +} +// codeblock-focus-end + +function HomeScreen({ navigation: { navigate } }) { + return ( + + This is the home screen of the app + + + ); +} + +function ProfileScreen() { + return ( + + Profile Screen + + + ); +} + +const Stack = createNativeStackNavigator(); + +function App() { + return ( + + + + + + ); } + +export default App; ``` + + + See the documentation for the [`navigation` object](navigation-object.md) for more info. ## Using with class component @@ -44,7 +158,7 @@ class MyBackButton extends React.Component { } // Wrap and export -export default function(props) { +export default function (props) { const navigation = useNavigation(); return ; diff --git a/versioned_docs/version-7.x/use-route.md b/versioned_docs/version-7.x/use-route.md index 0b5d74891af..22ea8c3effa 100755 --- a/versioned_docs/version-7.x/use-route.md +++ b/versioned_docs/version-7.x/use-route.md @@ -4,26 +4,144 @@ title: useRoute sidebar_label: useRoute --- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + `useRoute` is a hook which gives access to `route` object. It's useful when you cannot pass down the `route` object from props to the component, or don't want to pass it in case of a deeply nested child. `useRoute()` returns the `route` object of the screen it's inside. ## Example - + + -```js +```js name="useRoute hook" snack version=7 +import * as React from 'react'; +import { View, Text } from 'react-native'; +import { Button } from '@react-navigation/elements'; +import { + createStaticNavigation, + useNavigation, +} from '@react-navigation/native'; +import { createNativeStackNavigator } from '@react-navigation/native-stack'; +// codeblock-focus-start +import { useRoute } from '@react-navigation/native'; + +function MyText() { + // highlight-next-line + const route = useRoute(); + + return {route.params.caption}; +} +// codeblock-focus-end + +function HomeScreen() { + const navigation = useNavigation(); + + return ( + + This is the home screen of the app + + + ); +} + +function ProfileScreen() { + return ( + + Profile Screen + + + ); +} + +const Stack = createNativeStackNavigator({ + initialRouteName: 'Home', + screens: { + Home: HomeScreen, + Profile: ProfileScreen, + }, +}); + +const Navigation = createStaticNavigation(Stack); + +function App() { + return ; +} + +export default App; +``` + + + + +```js name="useRoute hook" snack version=7 import * as React from 'react'; -import { Text } from 'react-native'; +import { View, Text } from 'react-native'; +import { Button } from '@react-navigation/elements'; +import { NavigationContainer, useNavigation } from '@react-navigation/native'; +import { createNativeStackNavigator } from '@react-navigation/native-stack'; +// codeblock-focus-start import { useRoute } from '@react-navigation/native'; function MyText() { + // highlight-next-line const route = useRoute(); return {route.params.caption}; } +// codeblock-focus-end + +function HomeScreen({ navigation }) { + return ( + + This is the home screen of the app + + + ); +} + +function ProfileScreen() { + return ( + + Profile Screen + + + ); +} + +const Stack = createNativeStackNavigator(); + +function App() { + return ( + + + + + + + ); +} + +export default App; ``` + + + See the documentation for the [`route` object](route-object.md) for more info. ## Using with class component @@ -39,7 +157,7 @@ class MyText extends React.Component { } // Wrap and export -export default function(props) { +export default function (props) { const route = useRoute(); return ; diff --git a/versioned_docs/version-7.x/use-scroll-to-top.md b/versioned_docs/version-7.x/use-scroll-to-top.md index a4153d0dda1..23b353c8b4c 100755 --- a/versioned_docs/version-7.x/use-scroll-to-top.md +++ b/versioned_docs/version-7.x/use-scroll-to-top.md @@ -4,28 +4,148 @@ title: useScrollToTop sidebar_label: useScrollToTop --- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + The expected native behavior of scrollable components is to respond to events from navigation that will scroll to top when tapping on the active tab as you would expect from native tab bars. In order to achieve it we export `useScrollToTop` which accept ref to scrollable component (e,g. `ScrollView` or `FlatList`). Example: - + + -```js +```js name="useScrollToTop hook" snack version=7 +import * as React from 'react'; +import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; +import { createStaticNavigation } from '@react-navigation/native'; +import { View, Image } from 'react-native'; +// codeblock-focus-start +import { ScrollView } from 'react-native'; +import { useScrollToTop } from '@react-navigation/native'; + +function Albums() { + const ref = React.useRef(null); + + // highlight-next-line + useScrollToTop(ref); + + return ( + + {/* content */} + // codeblock-focus-end + + + + + // codeblock-focus-start + + ); +} +// codeblock-focus-end + +function HomeScreen() { + return ; +} + +const Tab = createBottomTabNavigator({ + Home: HomeScreen, + Albums: Albums, +}); + +const Navigation = createStaticNavigation(Tab); + +export default function App() { + return ; +} +``` + + + + +```js name="useScrollToTop hook" snack version=7 import * as React from 'react'; +import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; +import { View, Image } from 'react-native'; +import { NavigationContainer } from '@react-navigation/native'; +// codeblock-focus-start import { ScrollView } from 'react-native'; import { useScrollToTop } from '@react-navigation/native'; function Albums() { const ref = React.useRef(null); + // highlight-next-line useScrollToTop(ref); - return {/* content */}; + return ( + + {/* content */} + // codeblock-focus-end + + + + + // codeblock-focus-start + + ); +} +// codeblock-focus-end + +function HomeScreen() { + return ; +} + +const Tab = createBottomTabNavigator(); + +export default function App() { + return ( + + + + + + + ); } ``` + + + ## Using with class component You can wrap your class component in a function component to use the hook: @@ -51,10 +171,85 @@ export default function (props) { If you require offset to scroll position you can wrap and decorate passed reference: - + + -```js +```js name="useScrollToTop hook - providing scroll offset" snack version=7 +import * as React from 'react'; +import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; +import { View, Image } from 'react-native'; +import { createStaticNavigation } from '@react-navigation/native'; + +// codeblock-focus-start +import { ScrollView } from 'react-native'; +import { useScrollToTop } from '@react-navigation/native'; + +function Albums() { + const ref = React.useRef(null); + + useScrollToTop( + React.useRef({ + scrollToTop: () => ref.current?.scrollTo({ y: 100 }), + }) + ); + + return ( + + {/* content */} + // codeblock-focus-end + + + + + // codeblock-focus-start + + ); +} +// codeblock-focus-end + +function HomeScreen() { + return ; +} + +const Tab = createBottomTabNavigator({ + screens: { + Home: HomeScreen, + Albums: Albums, + }, +}); + +const Navigation = createStaticNavigation(Tab); + +export default function App() { + return ; +} +``` + + + + +```js name="useScrollToTop hook - providing scroll offset" snack version=7 import * as React from 'react'; +import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; +import { View, Image } from 'react-native'; +import { NavigationContainer } from '@react-navigation/native'; +// codeblock-focus-start import { ScrollView } from 'react-native'; import { useScrollToTop } from '@react-navigation/native'; @@ -67,6 +262,53 @@ function Albums() { }) ); - return {/* content */}; + return ( + + {/* content */} + // codeblock-focus-end + + + + + // codeblock-focus-start + + ); +} +// codeblock-focus-end + +function HomeScreen() { + return ; +} + +const Tab = createBottomTabNavigator(); + +export default function App() { + return ( + + + + + + + ); } ``` + + + diff --git a/versioned_docs/version-7.x/use-theme.md b/versioned_docs/version-7.x/use-theme.md index 7807e031e70..fcda6bc6203 100644 --- a/versioned_docs/version-7.x/use-theme.md +++ b/versioned_docs/version-7.x/use-theme.md @@ -4,17 +4,63 @@ title: useTheme sidebar_label: useTheme --- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + The `useTheme` hook lets us access the currently active theme. You can use it in your own components to have them respond to changes in the theme. - + + -```js +```js name="useTheme hook" snack version=7 import * as React from 'react'; -import { TouchableOpacity, Text } from 'react-native'; +import { + useNavigation, + createStaticNavigation, + DefaultTheme, + DarkTheme, +} from '@react-navigation/native'; +import { Button, View, Text, TouchableOpacity, Appearance } from 'react-native'; +import { createNativeStackNavigator } from '@react-navigation/native-stack'; +import { createDrawerNavigator } from '@react-navigation/drawer'; + +// codeblock-focus-start import { useTheme } from '@react-navigation/native'; -// Black background and white text in light theme, inverted on dark theme +// codeblock-focus-end + +function SettingsScreen({ route }) { + const navigation = useNavigation(); + const { user } = route.params; + const { colors } = useTheme(); + + return ( + + Settings Screen + + userParam: {JSON.stringify(user)} + +