Skip to main content
‹ Bar buttons
Hook

useNavigationButton()

A press on a button of the screen's top bar or toolbar.

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

useNavigationButton handles the press of a button of the screen's bar in the component, by the button's id. It is for a press that needs the state of the component, such as Share sending the text the user typed. The button itself is declared in the screen's options; the hook only listens. It is called in a component of the screen.

Usage​

src/screens/Note.tsx
import { useState } from 'react';
import { Share, TextInput } from 'react-native';
import {
defineScreen,
useNavigationButton,
} from '@apexnative/navigation';

export const NoteScreen = defineScreen({
component: Note,
options: (s) =>
s.rightButtons({
id: 'share',
icon: { ios: 'square.and.arrow.up', android: 'ic_share' },
}),
});

function Note() {
const [text, setText] = useState('');
useNavigationButton('share', () => Share.share({ message: text }));
return <TextInput value={text} onChangeText={setText} multiline />;
}

The share icon is in the bar from the first frame. A tap shares the text the user has typed so far: the handler of the last render is called, so text is current without useCallback or a ref.

Signature​

function useNavigationButton(
buttonId: string,
handler: (event: ButtonPressedEvent) => void
): void
Defined in src/api/hooks/screenHooks.ts ↗

buttonId: the id of the button in the options, 'share' above. The handler is called only for presses with this id, and only on this screen.

handler gets a ButtonPressedEvent:

  • buttonId: the id that was pressed.
  • selected: for a toggle button iOS, its new state; not set for other buttons.
  • screen: the screen, as { id, name }.

It returns nothing.

What it hears​

A press is told by its id, so the same hook handles everything in the bar that has one:

PressedThe id
A button of leftButtons, rightButtons, centerButtonsThe button's id.
A button of the toolbarThe button's id.
An item of a button's menu, at any depthThe item's id.
An item of the overflowMenuThe item's id.
The FAB and its actionsTheir id.

The ids of buttons and of menu items share one space on a screen: keep them different. A component as a button sends no press: its own onPress is the press.

A button of the app's default options, such as a Close on every modal, is on each screen; the hook in a screen hears the press on that screen's copy.

A toggle​

iOS A button with toggle: true switches on and off at each tap, and the event carries the new state:

useNavigationButton('favorite', ({ selected }) => {
setFavorite(selected ?? false); // your own state
});

onPress or the hook​

A button can also have onPress in the options. Both run when both are there; pick one per button.

The press needsUse
Nothing of the component: open a screen, a menuonPress in defineScreen options. It gets the screen's navigation.
The state of the componentuseNavigationButton in the component.
State, and the button changes with it (on, off)onPress in useScreenOptions: the options and the press are built from the same render.
To be heard by the whole app, for analyticsbuttonPressed in listeners.

Pitfalls​

  • The hook doesn't add the button. Without a button of that id in the options, nothing shows and the hook never runs. The id is a string; a typo is silent.
  • Only while mounted. The hook listens from the component's first effect. A button the user taps before a lazy part of the screen mounted reaches no handler; put the hook in the screen's top component.
  • One screen only. A tap on the same button id on another screen, even of the same name, doesn't reach it.
  • Outside a screen the hook throws "This hook is for the screens of createNavigation".

Compared with React Navigation​

In React Navigation the header buttons are React components (headerRight), set with navigation.setOptions inside an effect, so they see the state:

// React Navigation
useLayoutEffect(() => {
navigation.setOptions({
headerRight: () => (
<Button title="Share" onPress={() => share(text)} />
),
});
}, [navigation, text]);

Here the button is the platform's own, declared once in the options; the component handles its press, and nothing is set again when text changes.

See also​

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