diff --git a/example/StackNavigator.tsx b/example/StackNavigator.tsx index d2bf76294f06eb..ebc4d9733b17e9 100644 --- a/example/StackNavigator.tsx +++ b/example/StackNavigator.tsx @@ -262,11 +262,11 @@ const StackRouter: Router = { }; export function StackNavigator(props: Props) { - const { navigation, descriptors } = useNavigationBuilder(StackRouter, props); + const { state, descriptors } = useNavigationBuilder(StackRouter, props); return (
- {navigation.state.routes.map((route, i) => ( + {state.routes.map((route, i) => (
- { - descriptors[navigation.state.routes[navigation.state.index].key] - .options.title - } + {descriptors[state.routes[state.index].key].options.title}
); diff --git a/example/TabNavigator.tsx b/example/TabNavigator.tsx index ed5db03fcf83c4..1e80678a753461 100644 --- a/example/TabNavigator.tsx +++ b/example/TabNavigator.tsx @@ -184,18 +184,18 @@ const TabRouter: Router = { }; export function TabNavigator(props: Props) { - const { navigation, descriptors } = useNavigationBuilder(TabRouter, props); + const { state, descriptors } = useNavigationBuilder(TabRouter, props); return (
- {navigation.state.routes.map((route, i, self) => ( + {state.routes.map((route, i, self) => (
{descriptors[route.key].render()} diff --git a/example/index.tsx b/example/index.tsx index ace4e33be946d9..e8dc4f0d77f888 100644 --- a/example/index.tsx +++ b/example/index.tsx @@ -3,8 +3,9 @@ import { render } from 'react-dom'; import { NavigationContainer, CompositeNavigationProp, - NavigationHelpers, PartialState, + NavigationHelpers, + RouteProp, } from '../src'; import Stack, { StackNavigationProp } from './StackNavigator'; import Tab, { TabNavigationProp } from './TabNavigator'; @@ -26,14 +27,16 @@ const MyTab = Tab(); const First = ({ navigation, + route, }: { navigation: CompositeNavigationProp< StackNavigationProp, NavigationHelpers >; + route: RouteProp; }) => (
-

First, {navigation.state.params.author}

+

First, {route.params.author}

diff --git a/src/NavigationBuilderContext.tsx b/src/NavigationBuilderContext.tsx index 7a7e2cfc678267..5e4a0450292afd 100644 --- a/src/NavigationBuilderContext.tsx +++ b/src/NavigationBuilderContext.tsx @@ -7,7 +7,7 @@ export type ChildActionListener = ( ) => boolean; const NavigationBuilderContext = React.createContext<{ - helpers?: NavigationHelpers; + navigation?: NavigationHelpers; onAction?: (action: NavigationAction, sourceNavigatorKey?: string) => boolean; addActionListener?: (listener: ChildActionListener) => void; removeActionListener?: (listener: ChildActionListener) => void; diff --git a/src/SceneView.tsx b/src/SceneView.tsx index 2e8bb2d287fcbb..ca1fcdd0ccec0b 100644 --- a/src/SceneView.tsx +++ b/src/SceneView.tsx @@ -5,20 +5,20 @@ import { Route, NavigationState, NavigationHelpers, - ScreenProps, + RouteConfig, } from './types'; import EnsureSingleNavigator from './EnsureSingleNavigator'; type Props = { - screen: ScreenProps; - helpers: NavigationHelpers; + screen: RouteConfig; + navigation: NavigationHelpers; route: Route & { state?: NavigationState }; getState: () => NavigationState; setState: (state: NavigationState) => void; }; export default function SceneView(props: Props) { - const { screen, route, helpers, getState, setState } = props; + const { screen, route, navigation: helpers, getState, setState } = props; const navigation = React.useMemo( () => ({ @@ -35,9 +35,8 @@ export default function SceneView(props: Props) { ), }); }, - state: route, }), - [getState, helpers, route, setState] + [getState, helpers, route.key, setState] ); const getCurrentState = React.useCallback(() => { @@ -79,11 +78,12 @@ export default function SceneView(props: Props) { // @ts-ignore render={screen.component || screen.children} navigation={navigation} + route={route} > {'component' in screen && screen.component !== undefined ? ( - + ) : 'children' in screen && screen.children !== undefined ? ( - screen.children({ navigation }) + screen.children({ navigation, route }) ) : null} diff --git a/src/Screen.tsx b/src/Screen.tsx index db5e27e20dfb9e..955d18a12a15a2 100644 --- a/src/Screen.tsx +++ b/src/Screen.tsx @@ -1,6 +1,6 @@ -import { ScreenProps } from './types'; +import { RouteConfig } from './types'; -export default function Screen(_: ScreenProps) { +export default function Screen(_: RouteConfig) { /* istanbul ignore next */ return null; } diff --git a/src/__tests__/index.test.tsx b/src/__tests__/index.test.tsx index 1ea555c2371c64..5ca182083bb183 100644 --- a/src/__tests__/index.test.tsx +++ b/src/__tests__/index.test.tsx @@ -93,11 +93,9 @@ beforeEach(() => (MockRouter.key = 0)); it('initializes state for a navigator on navigation', () => { const TestNavigator = (props: any) => { - const { navigation, descriptors } = useNavigationBuilder(MockRouter, props); + const { state, descriptors } = useNavigationBuilder(MockRouter, props); - return descriptors[ - navigation.state.routes[navigation.state.index].key - ].render(); + return descriptors[state.routes[state.index].key].render(); }; const FooScreen = (props: any) => { @@ -148,11 +146,9 @@ it('initializes state for a navigator on navigation', () => { it('rehydrates state for a navigator on navigation', () => { const TestNavigator = (props: any) => { - const { navigation, descriptors } = useNavigationBuilder(MockRouter, props); + const { state, descriptors } = useNavigationBuilder(MockRouter, props); - return descriptors[ - navigation.state.routes[navigation.state.index].key - ].render(); + return descriptors[state.routes[state.index].key].render(); }; const BarScreen = (props: any) => { @@ -195,11 +191,9 @@ it('rehydrates state for a navigator on navigation', () => { it('initializes state for nested navigator on navigation', () => { const TestNavigator = (props: any) => { - const { navigation, descriptors } = useNavigationBuilder(MockRouter, props); + const { state, descriptors } = useNavigationBuilder(MockRouter, props); - return descriptors[ - navigation.state.routes[navigation.state.index].key - ].render(); + return descriptors[state.routes[state.index].key].render(); }; const TestScreen = (props: any) => { @@ -255,11 +249,9 @@ it('initializes state for nested navigator on navigation', () => { it("doesn't update state if nothing changed", () => { const TestNavigator = (props: any) => { - const { navigation, descriptors } = useNavigationBuilder(MockRouter, props); + const { state, descriptors } = useNavigationBuilder(MockRouter, props); - return descriptors[ - navigation.state.routes[navigation.state.index].key - ].render(); + return descriptors[state.routes[state.index].key].render(); }; const FooScreen = (props: any) => { @@ -287,11 +279,9 @@ it("doesn't update state if nothing changed", () => { it("doesn't update state if action wasn't handled", () => { const TestNavigator = (props: any) => { - const { navigation, descriptors } = useNavigationBuilder(MockRouter, props); + const { state, descriptors } = useNavigationBuilder(MockRouter, props); - return descriptors[ - navigation.state.routes[navigation.state.index].key - ].render(); + return descriptors[state.routes[state.index].key].render(); }; const FooScreen = (props: any) => { @@ -319,11 +309,9 @@ it("doesn't update state if action wasn't handled", () => { it('cleans up state when the navigator unmounts', () => { const TestNavigator = (props: any) => { - const { navigation, descriptors } = useNavigationBuilder(MockRouter, props); + const { state, descriptors } = useNavigationBuilder(MockRouter, props); - return descriptors[ - navigation.state.routes[navigation.state.index].key - ].render(); + return descriptors[state.routes[state.index].key].render(); }; const FooScreen = (props: any) => { @@ -383,22 +371,15 @@ it("lets parent handle the action if child didn't", () => { }; const ParentNavigator = (props: any) => { - const { navigation, descriptors } = useNavigationBuilder( - ParentRouter, - props - ); - - return descriptors[ - navigation.state.routes[navigation.state.index].key - ].render(); + const { state, descriptors } = useNavigationBuilder(ParentRouter, props); + + return descriptors[state.routes[state.index].key].render(); }; const ChildNavigator = (props: any) => { - const { navigation, descriptors } = useNavigationBuilder(MockRouter, props); + const { state, descriptors } = useNavigationBuilder(MockRouter, props); - return descriptors[ - navigation.state.routes[navigation.state.index].key - ].render(); + return descriptors[state.routes[state.index].key].render(); }; const TestScreen = (props: any) => { @@ -444,11 +425,9 @@ it("lets parent handle the action if child didn't", () => { it('allows arbitrary state updates by dispatching a function', () => { const TestNavigator = (props: any) => { - const { navigation, descriptors } = useNavigationBuilder(MockRouter, props); + const { state, descriptors } = useNavigationBuilder(MockRouter, props); - return descriptors[ - navigation.state.routes[navigation.state.index].key - ].render(); + return descriptors[state.routes[state.index].key].render(); }; const FooScreen = (props: any) => { @@ -490,11 +469,9 @@ it('allows arbitrary state updates by dispatching a function', () => { it('updates route params with setParams', () => { const TestNavigator = (props: any) => { - const { navigation, descriptors } = useNavigationBuilder(MockRouter, props); + const { state, descriptors } = useNavigationBuilder(MockRouter, props); - return descriptors[ - navigation.state.routes[navigation.state.index].key - ].render(); + return descriptors[state.routes[state.index].key].render(); }; let setParams: (params: object) => void = () => undefined; diff --git a/src/__tests__/useOnChildUpdate.test.tsx b/src/__tests__/useOnChildUpdate.test.tsx index bfff3e84a663bf..28672dfad74ddf 100644 --- a/src/__tests__/useOnChildUpdate.test.tsx +++ b/src/__tests__/useOnChildUpdate.test.tsx @@ -37,25 +37,17 @@ it("lets children handle the action if parent didn't", () => { }; const ChildNavigator = (props: any) => { - const { navigation, descriptors } = useNavigationBuilder( - ChildRouter, - props - ); + const { state, descriptors } = useNavigationBuilder(ChildRouter, props); - return descriptors[ - navigation.state.routes[navigation.state.index].key - ].render(); + return descriptors[state.routes[state.index].key].render(); }; const ParentNavigator = (props: any) => { - const { navigation, descriptors } = useNavigationBuilder( - ParentRouter, - props - ); + const { state, descriptors } = useNavigationBuilder(ParentRouter, props); return ( - {navigation.state.routes.map(route => descriptors[route.key].render())} + {state.routes.map(route => descriptors[route.key].render())} ); }; diff --git a/src/createNavigator.tsx b/src/createNavigator.tsx index 3c27458138aed2..0276f47efddc81 100644 --- a/src/createNavigator.tsx +++ b/src/createNavigator.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { ParamListBase, ScreenProps, TypedNavigator } from './types'; +import { ParamListBase, RouteConfig, TypedNavigator } from './types'; import Screen from './Screen'; export default function createNavigator>( @@ -12,7 +12,7 @@ export default function createNavigator>( return { Navigator: RawNavigator, Screen: Screen as React.ComponentType< - ScreenProps + RouteConfig >, }; }; diff --git a/src/types.tsx b/src/types.tsx index 7a6648ada7ff71..ae09e1014b14fe 100644 --- a/src/types.tsx +++ b/src/types.tsx @@ -204,14 +204,6 @@ export type NavigationProp< ParamList extends ParamListBase, RouteName extends keyof ParamList > = NavigationHelpers & { - /** - * State for the child navigator. - */ - state: Omit, 'params'> & - (ParamList[RouteName] extends undefined - ? {} - : { params: ParamList[RouteName] }); - /** * Update the param object for the route. * The new params will be shallow merged with the old one. @@ -221,6 +213,19 @@ export type NavigationProp< setParams(params: ParamList[RouteName]): void; }; +export type RouteProp< + ParamList extends ParamListBase, + RouteName extends keyof ParamList +> = Omit, 'params'> & + (ParamList[RouteName] extends undefined + ? {} + : { + /** + * Params for this route + */ + params: ParamList[RouteName]; + }); + export type CompositeNavigationProp< A extends NavigationHelpers, B extends NavigationHelpers @@ -251,7 +256,7 @@ export type Options = { [key: string]: any; }; -export type ScreenProps< +export type RouteConfig< ParamList extends ParamListBase = ParamListBase, RouteName extends keyof ParamList = string > = { @@ -295,5 +300,5 @@ export type TypedNavigator< initialRouteName?: keyof ParamList; } >; - Screen: React.ComponentType>; + Screen: React.ComponentType>; }; diff --git a/src/useDescriptors.tsx b/src/useDescriptors.tsx index caa8b69f8462fe..48114e8c5e8293 100644 --- a/src/useDescriptors.tsx +++ b/src/useDescriptors.tsx @@ -6,7 +6,7 @@ import { NavigationHelpers, NavigationState, ParamListBase, - ScreenProps, + RouteConfig, } from './types'; import SceneView from './SceneView'; import NavigationBuilderContext, { @@ -15,8 +15,8 @@ import NavigationBuilderContext, { type Options = { state: NavigationState | PartialState; - screens: { [key: string]: ScreenProps }; - helpers: NavigationHelpers; + screens: { [key: string]: RouteConfig }; + navigation: NavigationHelpers; onAction: (action: NavigationAction, sourceNavigatorKey?: string) => boolean; getState: () => NavigationState; setState: (state: NavigationState) => void; @@ -34,7 +34,7 @@ const EMPTY_OPTIONS = Object.freeze({}); export default function useDescriptors({ state, screens, - helpers, + navigation, onAction, getState, setState, @@ -44,13 +44,19 @@ export default function useDescriptors({ }: Options) { const context = React.useMemo( () => ({ - helpers, + navigation, onAction, addActionListener, removeActionListener, onChildUpdate, }), - [helpers, onAction, onChildUpdate, addActionListener, removeActionListener] + [ + navigation, + onAction, + onChildUpdate, + addActionListener, + removeActionListener, + ] ); return state.routes.reduce( @@ -62,7 +68,7 @@ export default function useDescriptors({ return ( { - acc[curr!.name] = curr as ScreenProps; + acc[curr!.name] = curr as RouteConfig; return acc; }, - {} as { [key: string]: ScreenProps } + {} as { [key: string]: RouteConfig } ); const routeNames = Object.keys(screens); @@ -140,22 +140,17 @@ export default function useNavigationBuilder( setState, }); - const helpers = useNavigationHelpers({ + const navigation = useNavigationHelpers({ onAction, getState, setState, actionCreators: router.actionCreators, }); - const navigation = React.useMemo(() => ({ ...helpers, state }), [ - helpers, - state, - ]); - const descriptors = useDescriptors({ state, screens, - helpers, + navigation, onAction, getState, setState, @@ -165,6 +160,7 @@ export default function useNavigationBuilder( }); return { + state, navigation, descriptors, }; diff --git a/src/useNavigationHelpers.tsx b/src/useNavigationHelpers.tsx index 48fd8bac03ca4a..f53ff9dabb269a 100644 --- a/src/useNavigationHelpers.tsx +++ b/src/useNavigationHelpers.tsx @@ -21,7 +21,7 @@ export default function useNavigationHelpers({ setState, actionCreators, }: Options) { - const { helpers: parentNavigationHelpers } = React.useContext( + const { navigation: parentNavigationHelpers } = React.useContext( NavigationBuilderContext );