mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-05-27 09:04:44 +08:00
This adds ability to listen to events from the component where the navigator is defined, even if the screen is not rendered.
```js
<Tabs.Screen
name="Chat"
component={Chat}
options={{ title: 'Chat' }}
listeners={{
tabPress: e => console.log('Tab press', e.target),
}}
/>
```
Closes #6756
193 lines
5.7 KiB
TypeScript
193 lines
5.7 KiB
TypeScript
import * as React from 'react';
|
|
import {
|
|
NavigationState,
|
|
PartialState,
|
|
ParamListBase,
|
|
TypedNavigator,
|
|
NavigationProp,
|
|
RouteProp,
|
|
EventMapBase,
|
|
} from '@react-navigation/native';
|
|
import CompatScreen from './CompatScreen';
|
|
import ScreenPropsContext from './ScreenPropsContext';
|
|
import createCompatNavigationProp from './createCompatNavigationProp';
|
|
import { CompatScreenType, CompatRouteConfig } from './types';
|
|
|
|
export default function createCompatNavigatorFactory<
|
|
CreateNavigator extends () => TypedNavigator<
|
|
ParamListBase,
|
|
NavigationState,
|
|
{},
|
|
EventMapBase,
|
|
React.ComponentType<any>
|
|
>
|
|
>(createNavigator: CreateNavigator) {
|
|
// @ts-ignore
|
|
if (createNavigator.isCompat) {
|
|
throw new Error(
|
|
`The navigator is already in compat mode. You don't need to wrap it in 'createCompatNavigatorFactory'.`
|
|
);
|
|
}
|
|
|
|
const createCompatNavigator = <
|
|
NavigationPropType extends NavigationProp<any, any, any, any, any>,
|
|
ParamList extends ParamListBase = NavigationPropType extends NavigationProp<
|
|
infer P
|
|
>
|
|
? P
|
|
: ParamListBase,
|
|
ScreenOptions extends {} = NavigationPropType extends NavigationProp<
|
|
any,
|
|
any,
|
|
any,
|
|
infer O
|
|
>
|
|
? O
|
|
: {},
|
|
NavigationConfig extends {} = React.ComponentProps<
|
|
ReturnType<CreateNavigator>['Navigator']
|
|
>
|
|
>(
|
|
routeConfig: CompatRouteConfig<NavigationPropType>,
|
|
navigationConfig: Partial<Omit<NavigationConfig, 'screenOptions'>> & {
|
|
order?: Extract<keyof ParamList, string>[];
|
|
defaultNavigationOptions?: ScreenOptions;
|
|
navigationOptions?: Record<string, any>;
|
|
} = {}
|
|
) => {
|
|
const Pair = createNavigator();
|
|
|
|
const {
|
|
order,
|
|
defaultNavigationOptions,
|
|
navigationOptions: parentNavigationOptions,
|
|
...restConfig
|
|
} = navigationConfig;
|
|
|
|
const routeNames = order !== undefined ? order : Object.keys(routeConfig);
|
|
|
|
function Navigator({ screenProps }: { screenProps?: unknown }) {
|
|
const screens = React.useMemo(
|
|
() =>
|
|
routeNames.map(name => {
|
|
let getScreenComponent: () => CompatScreenType<NavigationPropType>;
|
|
|
|
let initialParams;
|
|
|
|
const routeConfigItem = routeConfig[name];
|
|
|
|
if ('getScreen' in routeConfigItem) {
|
|
getScreenComponent = routeConfigItem.getScreen;
|
|
initialParams = routeConfigItem.params;
|
|
} else if ('screen' in routeConfigItem) {
|
|
getScreenComponent = () => routeConfigItem.screen;
|
|
initialParams = routeConfigItem.params;
|
|
} else {
|
|
getScreenComponent = () => routeConfigItem;
|
|
}
|
|
|
|
const screenOptions = ({
|
|
navigation,
|
|
route,
|
|
}: {
|
|
navigation: NavigationPropType;
|
|
route: RouteProp<ParamList, keyof ParamList> & {
|
|
state?: NavigationState | PartialState<NavigationState>;
|
|
};
|
|
}) => {
|
|
// @ts-ignore
|
|
const routeNavigationOptions = routeConfigItem.navigationOptions;
|
|
const screenNavigationOptions = getScreenComponent()
|
|
.navigationOptions;
|
|
|
|
if (
|
|
routeNavigationOptions == null &&
|
|
screenNavigationOptions == null
|
|
) {
|
|
return undefined;
|
|
}
|
|
|
|
const options =
|
|
typeof routeNavigationOptions === 'function' ||
|
|
typeof screenNavigationOptions === 'function'
|
|
? {
|
|
navigation: createCompatNavigationProp<
|
|
NavigationPropType
|
|
>(navigation, route, {}),
|
|
navigationOptions: defaultNavigationOptions || {},
|
|
screenProps,
|
|
}
|
|
: {};
|
|
|
|
return {
|
|
...(typeof routeNavigationOptions === 'function'
|
|
? routeNavigationOptions(options)
|
|
: routeNavigationOptions),
|
|
...(typeof screenNavigationOptions === 'function'
|
|
? (screenNavigationOptions as (o: any) => ScreenOptions)(
|
|
options
|
|
)
|
|
: screenNavigationOptions),
|
|
} as ScreenOptions;
|
|
};
|
|
|
|
return (
|
|
<Pair.Screen
|
|
key={name}
|
|
name={name}
|
|
initialParams={initialParams}
|
|
options={screenOptions}
|
|
>
|
|
{({ navigation, route }) => (
|
|
<CompatScreen
|
|
navigation={navigation}
|
|
route={route}
|
|
component={getScreenComponent()}
|
|
/>
|
|
)}
|
|
</Pair.Screen>
|
|
);
|
|
}),
|
|
[screenProps]
|
|
);
|
|
|
|
return (
|
|
<ScreenPropsContext.Provider value={screenProps}>
|
|
<Pair.Navigator
|
|
{...(restConfig as NavigationConfig)}
|
|
screenOptions={defaultNavigationOptions}
|
|
>
|
|
{screens}
|
|
</Pair.Navigator>
|
|
</ScreenPropsContext.Provider>
|
|
);
|
|
}
|
|
|
|
Navigator.navigationOtions = parentNavigationOptions;
|
|
|
|
return Navigator;
|
|
};
|
|
|
|
Object.defineProperties(createCompatNavigator, {
|
|
isCompat: {
|
|
get() {
|
|
return true;
|
|
},
|
|
},
|
|
router: {
|
|
get() {
|
|
throw new Error(
|
|
"It's no longer possible to access the router with the 'router' property."
|
|
);
|
|
},
|
|
set() {
|
|
throw new Error(
|
|
"It's no longer possible to override the router by assigning the 'router' property."
|
|
);
|
|
},
|
|
},
|
|
});
|
|
|
|
return createCompatNavigator;
|
|
}
|