Skip to main content
‹ Transitions
Hook

usePreview()

A long press on the view with nativeID in this screen (a right click with a pointer on iPad) shows screen small, with actions as a menu under it; a tap on the preview pushes the screen. UIKit's context menu. The preview goes when the component unmounts. Elsewhere the view has no preview.

SectionTransitionsDraftDocumented, not reviewed yet: the API can change.
Import
import { usePreview } from '@apexnative/navigation';

iOS usePreview gives a view of a screen the preview of the screen it opens: a long press on the view shows that screen small, with a menu under it, and a tap on the preview pushes the screen. It is UIKit's context menu, the one Photos and Mail show. A component of a screen calls it, with the nativeID of the view. On Android it does nothing.

Usage​

src/screens/Inbox.tsx
import { Pressable, Text } from 'react-native';
import { navigation, usePreview } from '@apexnative/navigation';

function Row({ id, subject }: { id: string; subject: string }) {
usePreview(`message-${id}`, {
screen: 'Details',
props: { id },
actions: [
{
id: 'archive',
title: 'Archive',
onPress: () => archive(id), // your own
},
{
id: 'delete',
title: 'Delete',
style: 'destructive',
onPress: () => remove(id), // your own
},
],
});
return (
<Pressable
nativeID={`message-${id}`}
onPress={() => navigation.push('Details', { props: { id } })}
>
<Text>{subject}</Text>
</Pressable>
);
}

A tap on a row pushes Details as usual. A long press lifts the row and shows Details small above it, with Archive and Delete under it. A tap on the preview pushes Details from there; a pick in the menu runs its onPress; a tap outside closes it all. With a pointer on iPad, a right click shows the same.

iOS

Signature​

function usePreview<Name extends ScreenName>(
nativeID: string,
config: PreviewConfig<Name>
): void
Defined in src/api/hooks/usePreview.ts ↗
  • nativeID: the nativeID of a view of the same screen. The view has to be there within a moment of the component mounting; it can be the component's own view, as above, or another one of the screen.
  • config: what the preview shows, below.
  • Returns nothing. The preview stays as long as the component is mounted, and goes when it unmounts.

What the preview shows​

screen​

screen: Name
TypeName Required.PlatformsiOSiPadOSAndroid

The screen to preview and push, by its name in screens. It is pushed onto the stack of the screen that calls the hook, so that screen has to be in a stack. Its options apply as on any push: the preview shows it with them, and it keeps them once pushed.

props​

props?: ScreenProps<Name>
TypeScreenProps<Name>PlatformsiOSiPadOSAndroid

The props of the screen, typed by its component, and required when the screen has required props, as in a push. A change of the props makes a new preview, with the screen rendered again.

actions​

actions?: PreviewAction[]
TypePreviewAction[]PlatformsiOSiPadOSAndroid

The menu under the preview. Each item is a PreviewAction:

  • id: the item's id, unique in the menu.
  • title: the text the user sees.
  • style: 'default', 'selected' with a checkmark, or 'destructive' in red.
  • onPress: runs when the user picks it. The menu closes; the screen isn't pushed.
  • actions: items of a submenu, shown when the user picks this one.
actions: [
{
id: 'move',
title: 'Move to',
actions: [
{ id: 'work', title: 'Work', onPress: () => move(id, 'work') },
{ id: 'home', title: 'Home', onPress: () => move(id, 'home') },
],
},
],

The onPress handlers are read when the user picks an item, so they can be new functions on every render; only a change of the ids, the titles or the styles makes a new menu. Without actions, the preview has no menu.

width​

width?: number
TypenumberPlatformsiOSiPadOSAndroid

The width of the preview in points. By default the width of the screen that calls the hook.

height​

height?: number
TypenumberPlatformsiOSiPadOSAndroid

The height of the preview in points. By default the height of the screen. A short screen, such as the first lines of a message, previews better with a height of its own:

usePreview(`message-${id}`, {
screen: 'Details',
props: { id },
height: 320,
});

When the screen renders​

The previewed screen is rendered when a press on the view starts, not when the component mounts. At the mount the native side only makes the screen's node, which is cheap: it has no React root yet. When the press starts, UIKit asks for the menu about half a second before it shows the preview, and that is when the screen gets its root and renders: its content is there when the preview shows.

A tap held for a moment (about 0.3 s, before the long press is recognized) starts the render too, even if the menu never opens. A right click with a pointer on iPad opens the menu at once, with no press before it: the first time, the screen renders as the preview shows, and its content can come a moment after the preview. A tap on the preview then waits for that content before it pushes the screen.

Once rendered, the screen stays rendered until the preview goes: the component unmounts, its props, screen or actions change, the screen with the view closes, or a tap pushes it. Closing the menu without a tap keeps the screen, so the next long press shows it at once. It renders like any screen, with its effects; it isn't on the stack until the user commits the preview. After a push, the view gets a new preview, which renders at the next press.

Two cases still render at the mount:

  • With appProvider, where every screen is in the app's one React tree, the screen renders in its place in that tree as the hook mounts.
  • A native screen, registered with a native name and made by the app's native creator, is made and rendered at the mount.

A screen with a key is no exception: the preview leaves the stack alone until the tap. If the stack has a screen with the same key by then, the tap goes back to that screen, as push does, and the preview goes.

So a usePreview costs a rendered screen only after the user presses its view. A list of a hundred rows with a preview each holds no rendered screens until one is pressed: no hidden React roots, and no renders or effects (network calls, subscriptions) of screens the user never opens. Each screen the user did press stays rendered until its preview goes, so keep the previewed screen light to render. With appProvider or native screens, which render at the mount, each usePreview costs one rendered screen while it is mounted; there, keep previews to what the user sees, such as the rows of a FlatList, which unmounts the rows that scroll away.

Android​

Android Android has no context menu with a preview. The hook does nothing there: a long press on the view does what the view itself does, and no screen is rendered. For a menu on Android, handle onLongPress on the view yourself.

Mistakes to avoid​

  • The view has no nativeID, or another one. The preview finds the view by it, in the screen that calls the hook; without a match, a long press does nothing.
  • Two views with the same nativeID in one screen: give each row its own, with the item's id in it.
  • A screen that isn't in a stack, such as a single screen as the root: there is nothing to push onto, and a tap on the preview pushes nothing. Put the screen in a stack.

See also​

Generated from the types and their comments. Edit the source ↗