-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[docs] Expo Router: add docs for missing API reference (expo#23355)
# Why - As mentioned by @amandeepmittal, we need docs for the hooks API expo#23243 # How - Added API ref and ported docs. - Added a URL switching component to help users understand how different runtimes work. <img width="643" alt="Screenshot 2023-07-06 at 5 34 02 PM" src="https://github.com/expo/expo/assets/9664363/19e824ef-d22e-460e-9a2a-08fc956e872a"> <img width="639" alt="Screenshot 2023-07-06 at 5 34 08 PM" src="https://github.com/expo/expo/assets/9664363/a813891c-2fae-4179-b379-e1bc0ed39b01"> <img width="636" alt="Screenshot 2023-07-06 at 5 34 16 PM" src="https://github.com/expo/expo/assets/9664363/db96627f-93b1-4da0-a148-0d86eb59cd29"> <img width="637" alt="Screenshot 2023-07-06 at 5 34 21 PM" src="https://github.com/expo/expo/assets/9664363/38151aeb-7f62-480d-ac82-b37d05d3d962"> --------- Co-authored-by: Aman Mittal <[email protected]> Co-authored-by: Bartosz Kaszubowski <[email protected]>
- Loading branch information
1 parent
0a10144
commit 343338e
Showing
7 changed files
with
412 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
--- | ||
title: Hooks API | ||
description: Learn how to interact with the in-app URL in Expo Router. | ||
--- | ||
|
||
import { FileTree } from '~/ui/components/FileTree'; | ||
import { RouteUrlGroup, RouteUrl } from '~/ui/components/RouteUrl'; | ||
|
||
<RouteUrlGroup> | ||
|
||
In Expo Router, there's always a valid URL that represents the currently focused route. Use hooks to observe changes and interact with the URL. | ||
|
||
## `usePathname` | ||
|
||
Returns the currently selected route location without search parameters. For example, `/acme?foo=bar` -> `/acme`. Segments will be normalized: `/[id]?id=normal` -> `/normal` | ||
|
||
<RouteUrl>/profile/baconbrix?extra=info</RouteUrl> | ||
|
||
```js app/profile/[user].tsx | ||
import { Text } from 'react-native'; | ||
/* @info */ | ||
import { usePathname } from 'expo-router'; | ||
/* @end */ | ||
|
||
export default function Route() { | ||
/* @info <b>pathname = "/profile/baconbrix"</b> */ | ||
const pathname = usePathname(); | ||
/* @end */ | ||
|
||
return <Text>User: {user}</Text>; | ||
} | ||
``` | ||
|
||
## `useLocalSearchParams` | ||
|
||
Returns the URL search parameters for the contextually selected route. Refer to the [local vs. global search params](/router/reference/search-parameters/#local-vs-global-search-parameters) guide for more information. | ||
|
||
<FileTree files={['app/_layout.tsx', 'app/[first]/home.tsx', 'app/[second]/shop.tsx']} /> | ||
|
||
When `/abc/home` pushes `/123/shop`, `useGlobalSearchParams` returns `{ first: undefined, second: '123' }` on **/app/[first]/home.tsx** because the global URL has changed. However, you may want the params to remain `{ first: 'abc' }` to reflect the context of the screen. In this case, you can use `useLocalSearchParams` to ensure the params `{ first: 'abc' }` are still returned in **/app/[first]/home.tsx**. | ||
|
||
<RouteUrl>/profile/baconbrix?extra=info</RouteUrl> | ||
|
||
```js app/profile/[user].tsx | ||
import { Text } from 'react-native'; | ||
/* @info */ | ||
import { useLocalSearchParams } from 'expo-router'; | ||
/* @end */ | ||
|
||
export default function Route() { | ||
/* @info */ | ||
const { user, extra } = useLocalSearchParams(); | ||
/* @end */ | ||
return <Text>User: {user}</Text>; | ||
} | ||
``` | ||
|
||
## `useGlobalSearchParams` | ||
|
||
Returns the URL search parameters for the globally selected route. For example, `/acme?foo=bar` -> `{ foo: "bar" }`. | ||
|
||
Refer to the [local vs global search params](/router/reference/search-parameters/#local-vs-global-search-parameters) guide for more info. | ||
|
||
<RouteUrl>/profile/baconbrix?extra=info</RouteUrl> | ||
|
||
```js app/profile/[user].tsx | ||
import { Text } from 'react-native'; | ||
/* @info */ | ||
import { useGlobalSearchParams } from 'expo-router'; | ||
/* @end */ | ||
|
||
export default function Route() { | ||
/* @info <b>user=baconbrix</b> & <b>extra=info</b> */ | ||
const { user, extra } = useGlobalSearchParams(); | ||
/* @end */ | ||
return <Text>User: {user}</Text>; | ||
} | ||
``` | ||
|
||
### `Href` type | ||
|
||
The `Href` type is a union of the following types: | ||
|
||
- **string**: A full path like `/profile/settings` or a relative path like `../settings`. | ||
- **object**: An object with a `pathname` and optional `params` object. The `pathname` can be a full path like `/profile/settings` or a relative path like `../settings`. The `params` can be an object of key/value pairs. | ||
|
||
## `useSegments` | ||
|
||
Returns a list of segments for the currently selected route. Segments are not normalized so that they will be the same as the file path. For example, `/[id]?id=normal` -> `["[id]"]`. | ||
|
||
```js app/profile/[user].tsx | ||
import { Text } from 'react-native'; | ||
/* @info */ | ||
import { useSegments } from 'expo-router'; | ||
/* @end */ | ||
|
||
export default function Route() { | ||
/* @info <b>segments = ["profile", "[user]"]</b> */ | ||
const segments = useSegments(); | ||
/* @end */ | ||
return <Text>Hello</Text>; | ||
} | ||
``` | ||
|
||
This function can be typed using an abstract of string arrays: | ||
|
||
```js app/profile/[user].tsx | ||
import { useSegments } from 'expo-router'; | ||
|
||
export default function Route() { | ||
/* @info */ | ||
const segments = useSegments<['profile'] | ['profile', '[user]']>(); | ||
/* @end */ | ||
|
||
return </> | ||
} | ||
``` | ||
|
||
## `useNavigation` | ||
|
||
Access the underlying React Navigation [`navigation` prop](https://reactnavigation.org/docs/navigation-prop) to imperatively access layout-specific functionality like `navigation.openDrawer()` in a Drawer layout. [Learn more](https://reactnavigation.org/docs/navigation-prop/#navigator-dependent-functions). | ||
|
||
```js | ||
/* @info */ | ||
import { useNavigation } from 'expo-router'; | ||
/* @end */ | ||
|
||
export default function Route() { | ||
/* @info Access the current navigation object for the current route */ | ||
const navigation = useNavigation(); | ||
/* @end */ | ||
return ( | ||
<View> | ||
<Text | ||
onPress={() => { | ||
/* @info Open the drawer view */ | ||
navigation.openDrawer(); | ||
/* @end */ | ||
}}> | ||
Open Drawer | ||
</Text> | ||
</View> | ||
); | ||
} | ||
``` | ||
|
||
## `useFocusEffect` | ||
|
||
Given a function, the `useFocusEffect` hook will invoke the function whenever the route is "focused". | ||
|
||
```js | ||
/* @info */ | ||
import { useFocusEffect } from 'expo-router'; | ||
/* @end */ | ||
|
||
export default function Route() { | ||
|
||
useFocusEffect(() => { | ||
/* @info Invoked whenever the route is focused */ | ||
console.log('Hello') | ||
/* @end */ | ||
}) | ||
|
||
return </> | ||
} | ||
``` | ||
|
||
</RouteUrlGroup> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
--- | ||
title: Redirects | ||
description: Learn how redirect URLs in Expo Router. | ||
--- | ||
|
||
You can redirect a request to a different URL based on some in-app criteria. Expo Router supports a number of different redirection patterns. | ||
|
||
## Redirect | ||
|
||
You can immediately redirect from a particular screen by using the `Redirect` component: | ||
|
||
```js | ||
import { View, Text } from 'react-native'; | ||
/* @info */ | ||
import { Redirect } from 'expo-router'; | ||
/* @end */ | ||
|
||
export default function Page() { | ||
/* @info Some logic to determine if the user is logged in. */ | ||
const { user } = useAuth(); | ||
/* @end */ | ||
|
||
if (!user) { | ||
/* @info Redirect to the login screen if the user is not authenticated. */ | ||
return <Redirect href="/login" />; | ||
/* @end */ | ||
} | ||
|
||
return ( | ||
<View> | ||
<Text>Welcome Back!</Text> | ||
</View> | ||
); | ||
} | ||
``` | ||
|
||
You can also redirect imperatively with the `useRouter` hook: | ||
|
||
```js | ||
import { Text } from 'react-native'; | ||
import { useRouter, useFocusEffect } from 'expo-router'; | ||
|
||
function MyScreen() { | ||
const router = useRouter(); | ||
|
||
useFocusEffect(() => { | ||
// Call the replace method to redirect to a new route without adding to the history. | ||
// We do this in a useFocusEffect to ensure the redirect happens every time the screen | ||
// is focused. | ||
router.replace('/profile/settings'); | ||
}); | ||
|
||
return <Text>My Screen</Text>; | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import { css } from '@emotion/react'; | ||
import { ExpoGoLogo, shadows, theme, typography } from '@expo/styleguide'; | ||
import { breakpoints, spacing } from '@expo/styleguide-base'; | ||
import { ChevronDownIcon, Monitor01DuotoneIcon, Phone01DuotoneIcon } from '@expo/styleguide-icons'; | ||
import { useEffect, useState } from 'react'; | ||
|
||
type PopupActionProps<T extends string> = { | ||
items: { name: string; id: T }[]; | ||
selected: string; | ||
onSelect: (value: T) => void; | ||
}; | ||
export function RuntimePopup<T extends string>({ items, selected, onSelect }: PopupActionProps<T>) { | ||
const Icon = [ExpoGoLogo, Phone01DuotoneIcon, Monitor01DuotoneIcon][ | ||
items.findIndex(item => item.id === selected) | ||
]; | ||
const [isLoaded, setLoaded] = useState(false); | ||
|
||
useEffect(function didMount() { | ||
setLoaded(true); | ||
}, []); | ||
|
||
return ( | ||
<div className="relative"> | ||
<select | ||
aria-label="Runtime URL format selector" | ||
title="Select runtime URL format" | ||
css={selectStyle} | ||
className="focus-visible:-outline-offset-2 border-0 rounded-none border-l border-l-default h-10 leading-10 px-10 hocus:bg-subtle hocus:shadow-none" | ||
value={selected} | ||
onChange={e => { | ||
onSelect(e.target.value as T); | ||
}}> | ||
{items.map((item, index) => ( | ||
<option key={String(index)} value={item.id}> | ||
{item.name} | ||
</option> | ||
))} | ||
</select> | ||
{isLoaded && ( | ||
<div | ||
style={{ lineHeight: 1.3 }} | ||
className="absolute inset-x-2.5 inset-y-0 flex items-center justify-between gap-2 text-icon-secondary pointer-events-none select-none"> | ||
<Icon className={ICON_CLASSES} /> | ||
<ChevronDownIcon className="icon-xs text-icon-secondary pointer-events-none" /> | ||
</div> | ||
)} | ||
</div> | ||
); | ||
} | ||
|
||
const ICON_CLASSES = 'icon-sm text-icon-secondary pointer-events-none inline-block'; | ||
|
||
const selectStyle = css` | ||
${typography.fontSizes[14]} | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
color: ${theme.text.default}; | ||
line-height: 1.3; | ||
padding: 0 ${spacing[8]}px; | ||
color: ${theme.text.default}; | ||
text-indent: 0; | ||
box-shadow: ${shadows.xs}; | ||
-moz-appearance: none; | ||
-webkit-appearance: none; | ||
appearance: none; | ||
background-color: ${theme.background.default}; | ||
cursor: pointer; | ||
:hover { | ||
background-color: ${theme.background.element}; | ||
} | ||
:focus-visible { | ||
background-color: ${theme.background.element}; | ||
} | ||
@media screen and (max-width: ${(breakpoints.medium + breakpoints.large) / 2}px) { | ||
padding: 0 0; | ||
text-indent: -9999px; | ||
} | ||
`; |
Oops, something went wrong.