Skip to content

Commit c7b1810

Browse files
committed
Update custom navigator docs to use render instead of NavigationContent
1 parent 578413c commit c7b1810

5 files changed

Lines changed: 107 additions & 78 deletions

File tree

‎versioned_docs/version-7.x/custom-navigators.md‎

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,15 @@ import {
3333
} from '@react-navigation/native';
3434

3535
function MyNavigator(props) {
36-
const { state, descriptors, NavigationContent } = useNavigationBuilder(
36+
const { state, descriptors, render } = useNavigationBuilder(
3737
StackRouter,
3838
props
3939
);
4040

4141
const focusedRoute = state.routes[state.index];
4242
const descriptor = descriptors[focusedRoute.key];
4343

44-
return <NavigationContent>{descriptor.render()}</NavigationContent>;
44+
return render(descriptor.render());
4545
}
4646

4747
export const createMyNavigator = createNavigatorFactory(MyNavigator);
@@ -58,7 +58,7 @@ Let's break this down:
5858
- The hook returns the [navigation state](navigation-state.md) in the `state` property. This is the current state of the navigator. There's also a `descriptors` object which contains the data and helpers for each screen in the navigator.
5959
- We get the focused route from the state with `state.routes[state.index]` - as `state.index` is the index of the currently focused route in the `state.routes` array.
6060
- Then we get the corresponding descriptor for the focused route with `descriptors[focusedRoute.key]` and call the `render()` method on it to get the React element for the screen.
61-
- The content of the navigator is wrapped in `NavigationContent` to provide appropriate context and wrappers.
61+
- We use the `render` function returned by `useNavigationBuilder` to render the content of the navigator with appropriate context and wrappers.
6262

6363
With this, we have a basic stack navigator that renders only the focused screen. Unlike the built-in stack navigator, this doesn't keep unfocused screens rendered. But you can loop through `state.routes` and render all of the screens if you want to keep them mounted. You can also read `descriptor.options` to get the [options](screen-options.md) to handle the screen's title, header, and other options.
6464

@@ -90,6 +90,7 @@ The hook returns an object with following properties:
9090
- `navigation` - The navigation object for the screen. You don't need to pass this to the screen manually. But it's useful if we're rendering components outside the screen that need to receive `navigation` prop as well, such as a header component.
9191
- `options` - A getter which returns the options such as `title` for the screen if they are specified.
9292
- `render` - A function which can be used to render the actual screen. Calling `descriptors[route.key].render()` will return a React element containing the screen content. It's important to use this method to render a screen, otherwise any child navigators won't be connected to the navigation tree properly.
93+
- `render` - A function to render the navigator's content with context and wrappers necessary for the navigator to work.
9394

9495
Example:
9596

@@ -103,11 +104,13 @@ import {
103104
} from '@react-navigation/native';
104105

105106
function TabNavigator({ tabBarStyle, contentStyle, ...rest }) {
106-
const { state, navigation, descriptors, NavigationContent } =
107-
useNavigationBuilder(TabRouter, rest);
107+
const { state, navigation, descriptors, render } = useNavigationBuilder(
108+
TabRouter,
109+
rest
110+
);
108111

109-
return (
110-
<NavigationContent>
112+
return render(
113+
<>
111114
<View style={[{ flexDirection: 'row' }, tabBarStyle]}>
112115
{state.routes.map((route, index) => (
113116
<Pressable
@@ -148,7 +151,7 @@ function TabNavigator({ tabBarStyle, contentStyle, ...rest }) {
148151
);
149152
})}
150153
</View>
151-
</NavigationContent>
154+
</>
152155
);
153156
}
154157
```
@@ -357,7 +360,7 @@ type Props = DefaultNavigatorOptions<
357360
MyNavigationConfig;
358361

359362
function TabNavigator({ tabBarStyle, contentStyle, ...rest }: Props) {
360-
const { state, navigation, descriptors, NavigationContent } =
363+
const { state, navigation, descriptors, render } =
361364
// Generic parameters containing state, options, actions, events etc. types.
362365
useNavigationBuilder<
363366
TabNavigationState<ParamListBase>,
@@ -367,8 +370,8 @@ function TabNavigator({ tabBarStyle, contentStyle, ...rest }: Props) {
367370
MyNavigationEventMap
368371
>(TabRouter, rest);
369372

370-
return (
371-
<NavigationContent>
373+
return render(
374+
<>
372375
<View style={[{ flexDirection: 'row' }, tabBarStyle]}>
373376
{state.routes.map((route, index) => (
374377
<Pressable
@@ -414,7 +417,7 @@ function TabNavigator({ tabBarStyle, contentStyle, ...rest }: Props) {
414417
);
415418
})}
416419
</View>
417-
</NavigationContent>
420+
</>
418421
);
419422
}
420423

@@ -481,8 +484,9 @@ function MyBottomTabNavigator({
481484
UNSTABLE_router,
482485
...rest
483486
}) {
484-
const { state, descriptors, navigation, NavigationContent } =
485-
useNavigationBuilder(TabRouter, {
487+
const { state, descriptors, navigation, render } = useNavigationBuilder(
488+
TabRouter,
489+
{
486490
id,
487491
initialRouteName,
488492
backBehavior,
@@ -493,17 +497,16 @@ function MyBottomTabNavigator({
493497
screenOptions,
494498
screenLayout,
495499
UNSTABLE_router,
496-
});
497-
498-
return (
499-
<NavigationContent>
500-
<BottomTabView
501-
{...rest}
502-
state={state}
503-
navigation={navigation}
504-
descriptors={descriptors}
505-
/>
506-
</NavigationContent>
500+
}
501+
);
502+
503+
return render(
504+
<BottomTabView
505+
{...rest}
506+
state={state}
507+
navigation={navigation}
508+
descriptors={descriptors}
509+
/>
507510
);
508511
}
509512

@@ -521,8 +524,9 @@ import MyRouter from './MyRouter';
521524

522525
// ...
523526

524-
const { state, descriptors, navigation, NavigationContent } =
525-
useNavigationBuilder(MyRouter, {
527+
const { state, descriptors, navigation, render } = useNavigationBuilder(
528+
MyRouter,
529+
{
526530
id,
527531
initialRouteName,
528532
backBehavior,
@@ -532,7 +536,8 @@ const { state, descriptors, navigation, NavigationContent } =
532536
screenListeners,
533537
screenOptions,
534538
screenLayout,
535-
});
539+
}
540+
);
536541

537542
// ...
538543
```

‎versioned_docs/version-7.x/testing.md‎

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -807,21 +807,19 @@ import {
807807
import { View } from 'react-native';
808808

809809
function TestStackNavigator(props) {
810-
const { state, descriptors, NavigationContent } = useNavigationBuilder(
810+
const { state, descriptors, render } = useNavigationBuilder(
811811
StackRouter,
812812
props
813813
);
814814

815-
return (
816-
<NavigationContent>
817-
{state.routes.map((route, index) => {
818-
return (
819-
<View key={route.key} aria-hidden={index !== state.index}>
820-
{descriptors[route.key].render()}
821-
</View>
822-
);
823-
})}
824-
</NavigationContent>
815+
return render(
816+
state.routes.map((route, index) => {
817+
return (
818+
<View key={route.key} aria-hidden={index !== state.index}>
819+
{descriptors[route.key].render()}
820+
</View>
821+
);
822+
})
825823
);
826824
}
827825

‎versioned_docs/version-8.x/custom-navigators.md‎

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,15 @@ import {
3333
} from '@react-navigation/native';
3434

3535
function MyNavigator(props) {
36-
const { state, descriptors, NavigationContent } = useNavigationBuilder(
36+
const { state, descriptors, render } = useNavigationBuilder(
3737
StackRouter,
3838
props
3939
);
4040

4141
const focusedRoute = state.routes[state.index];
4242
const descriptor = descriptors[focusedRoute.key];
4343

44-
return <NavigationContent>{descriptor.render()}</NavigationContent>;
44+
return render(descriptor.render());
4545
}
4646

4747
export const createMyNavigator = createNavigatorFactory(MyNavigator);
@@ -58,7 +58,7 @@ Let's break this down:
5858
- The hook returns the [navigation state](navigation-state.md) in the `state` property. This is the current state of the navigator. There's also a `descriptors` object which contains the data and helpers for each screen in the navigator.
5959
- We get the focused route from the state with `state.routes[state.index]` - as `state.index` is the index of the currently focused route in the `state.routes` array.
6060
- Then we get the corresponding descriptor for the focused route with `descriptors[focusedRoute.key]` and call the `render()` method on it to get the React element for the screen.
61-
- The content of the navigator is wrapped in `NavigationContent` to provide appropriate context and wrappers.
61+
- We use the `render` function returned by `useNavigationBuilder` to render the content of the navigator with appropriate context and wrappers.
6262

6363
With this, we have a basic stack navigator that renders only the focused screen. Unlike the built-in stack navigator, this doesn't keep unfocused screens rendered. But you can loop through `state.routes` and render all of the screens if you want to keep them mounted. You can also read `descriptor.options` to get the [options](screen-options.md) to handle the screen's title, header, and other options.
6464

@@ -90,6 +90,7 @@ The hook returns an object with following properties:
9090
- `navigation` - The navigation object for the screen. You don't need to pass this to the screen manually. But it's useful if we're rendering components outside the screen that need to receive `navigation` prop as well, such as a header component.
9191
- `options` - A getter which returns the options such as `title` for the screen if they are specified.
9292
- `render` - A function which can be used to render the actual screen. Calling `descriptors[route.key].render()` will return a React element containing the screen content. It's important to use this method to render a screen, otherwise any child navigators won't be connected to the navigation tree properly.
93+
- `render` - A function to render the navigator's content with context and wrappers necessary for the navigator to work.
9394

9495
Example:
9596

@@ -103,11 +104,13 @@ import {
103104
} from '@react-navigation/native';
104105

105106
function TabNavigator({ tabBarStyle, contentStyle, ...rest }) {
106-
const { state, navigation, descriptors, NavigationContent } =
107-
useNavigationBuilder(TabRouter, rest);
107+
const { state, navigation, descriptors, render } = useNavigationBuilder(
108+
TabRouter,
109+
rest
110+
);
108111

109-
return (
110-
<NavigationContent>
112+
return render(
113+
<>
111114
<View style={[{ flexDirection: 'row' }, tabBarStyle]}>
112115
{state.routes.map((route, index) => (
113116
<Pressable
@@ -147,7 +150,7 @@ function TabNavigator({ tabBarStyle, contentStyle, ...rest }) {
147150
);
148151
})}
149152
</View>
150-
</NavigationContent>
153+
</>
151154
);
152155
}
153156
```
@@ -341,7 +344,7 @@ type Props = DefaultNavigatorOptions<
341344
MyNavigationConfig;
342345

343346
function TabNavigator({ tabBarStyle, contentStyle, ...rest }: Props) {
344-
const { state, navigation, descriptors, NavigationContent } =
347+
const { state, navigation, descriptors, render } =
345348
// Generic parameters containing state, options, actions, events etc. types.
346349
useNavigationBuilder<
347350
TabNavigationState<ParamListBase>,
@@ -351,8 +354,8 @@ function TabNavigator({ tabBarStyle, contentStyle, ...rest }: Props) {
351354
MyNavigationEventMap
352355
>(TabRouter, rest);
353356

354-
return (
355-
<NavigationContent>
357+
return render(
358+
<>
356359
<View style={[{ flexDirection: 'row' }, tabBarStyle]}>
357360
{state.routes.map((route, index) => (
358361
<Pressable
@@ -395,7 +398,7 @@ function TabNavigator({ tabBarStyle, contentStyle, ...rest }: Props) {
395398
);
396399
})}
397400
</View>
398-
</NavigationContent>
401+
</>
399402
);
400403
}
401404

@@ -448,8 +451,9 @@ function MyBottomTabNavigator({
448451
router,
449452
...rest
450453
}) {
451-
const { state, descriptors, navigation, NavigationContent } =
452-
useNavigationBuilder(TabRouter, {
454+
const { state, descriptors, navigation, render } = useNavigationBuilder(
455+
TabRouter,
456+
{
453457
initialRouteName,
454458
backBehavior,
455459
routeNamesChangeBehavior,
@@ -459,17 +463,16 @@ function MyBottomTabNavigator({
459463
screenOptions,
460464
screenLayout,
461465
router,
462-
});
463-
464-
return (
465-
<NavigationContent>
466-
<BottomTabView
467-
{...rest}
468-
state={state}
469-
navigation={navigation}
470-
descriptors={descriptors}
471-
/>
472-
</NavigationContent>
466+
}
467+
);
468+
469+
return render(
470+
<BottomTabView
471+
{...rest}
472+
state={state}
473+
navigation={navigation}
474+
descriptors={descriptors}
475+
/>
473476
);
474477
}
475478

@@ -486,8 +489,9 @@ import MyRouter from './MyRouter';
486489

487490
// ...
488491

489-
const { state, descriptors, navigation, NavigationContent } =
490-
useNavigationBuilder(MyRouter, {
492+
const { state, descriptors, navigation, render } = useNavigationBuilder(
493+
MyRouter,
494+
{
491495
initialRouteName,
492496
backBehavior,
493497
routeNamesChangeBehavior,
@@ -496,7 +500,8 @@ const { state, descriptors, navigation, NavigationContent } =
496500
screenListeners,
497501
screenOptions,
498502
screenLayout,
499-
});
503+
}
504+
);
500505

501506
// ...
502507
```

‎versioned_docs/version-8.x/testing.md‎

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -807,21 +807,19 @@ import {
807807
import { View } from 'react-native';
808808

809809
function TestStackNavigator(props) {
810-
const { state, descriptors, NavigationContent } = useNavigationBuilder(
810+
const { state, descriptors, render } = useNavigationBuilder(
811811
StackRouter,
812812
props
813813
);
814814

815-
return (
816-
<NavigationContent>
817-
{state.routes.map((route, index) => {
818-
return (
819-
<View key={route.key} aria-hidden={index !== state.index}>
820-
{descriptors[route.key].render()}
821-
</View>
822-
);
823-
})}
824-
</NavigationContent>
815+
return render(
816+
state.routes.map((route, index) => {
817+
return (
818+
<View key={route.key} aria-hidden={index !== state.index}>
819+
{descriptors[route.key].render()}
820+
</View>
821+
);
822+
})
825823
);
826824
}
827825

‎versioned_docs/version-8.x/upgrading-from-7.x.md‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,29 @@ See [Custom navigators](custom-navigators.md) for more details.
219219

220220
### Changes to navigators
221221

222+
#### Custom navigators need to use the `render` callback
223+
224+
Previously, `useNavigationBuilder` returned a `NavigationContent` component for wrapping the navigator's content. The API was problematic, as `NavigationContent` needed to be stable despite using dynamic data. The approach we used to achieve this was not compatible with concurrent rendering.
225+
226+
To solve this properly, we replaced it with a `render` callback that takes the navigator's content as an argument and returns a React element:
227+
228+
```diff lang=js
229+
- const { state, descriptors, NavigationContent } = useNavigationBuilder(
230+
+ const { state, descriptors, render } = useNavigationBuilder(
231+
Router,
232+
props
233+
);
234+
235+
- return (
236+
- <NavigationContent>
237+
- <NavigatorView />
238+
- </NavigationContent>
239+
- );
240+
+ return render(<NavigatorView />);
241+
```
242+
243+
See [Custom navigators](custom-navigators.md) for more details.
244+
222245
#### Native Bottom Tabs are now default
223246

224247
Previously, the Bottom Tab Navigator used a JavaScript-based implementation and a native implementation was available under `@react-navigation/bottom-tabs/unstable`. The `@react-navigation/bottom-tabs/unstable` entry point has been removed and it has been merged into the main package.

0 commit comments

Comments
 (0)