mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-05-08 02:48:47 +08:00
The `isFirstRouteInParent` method was added to determine whether the back button should be shown in the header for stack navigator or not. This was mainly due to the API of the old version of stack whose public API of header didn't have all required info to determine whether it should be shown. It was probably a mistake to add it, because this method doesn't look at history and so pretty much useless for other navigators which aren't stack. In the new stack, the header receives a `previous` prop and the `headerLeft` option receives a `canGoBack` prop which can be used for this purpose. It's also possible to check if a route is the first by doing `navigation.dangerouslyGetState().routes[0].key === route.key`. So it's time to drop this method.
196 lines
5.2 KiB
TypeScript
196 lines
5.2 KiB
TypeScript
import {
|
|
NavigationState,
|
|
PartialState,
|
|
ParamListBase,
|
|
NavigationProp,
|
|
RouteProp,
|
|
} from '@react-navigation/core';
|
|
import * as helpers from './helpers';
|
|
import { CompatNavigationProp } from './types';
|
|
|
|
type EventName = 'willFocus' | 'willBlur' | 'didFocus' | 'didBlur' | 'refocus';
|
|
|
|
const focusSubscriptions = new WeakMap<() => void, () => void>();
|
|
const blurSubscriptions = new WeakMap<() => void, () => void>();
|
|
const refocusSubscriptions = new WeakMap<() => void, () => void>();
|
|
|
|
export default function createCompatNavigationProp<
|
|
NavigationPropType extends NavigationProp<ParamListBase>,
|
|
ParamList extends ParamListBase = NavigationPropType extends NavigationProp<
|
|
infer P
|
|
>
|
|
? P
|
|
: ParamListBase
|
|
>(
|
|
navigation: NavigationPropType,
|
|
state:
|
|
| (RouteProp<ParamList, keyof ParamList> & {
|
|
state?: NavigationState | PartialState<NavigationState>;
|
|
})
|
|
| NavigationState
|
|
| PartialState<NavigationState>
|
|
): CompatNavigationProp<NavigationPropType> {
|
|
return {
|
|
...navigation,
|
|
...Object.entries(helpers).reduce<{
|
|
[key: string]: (...args: any[]) => void;
|
|
}>((acc, [name, method]: [string, Function]) => {
|
|
if (name in navigation) {
|
|
acc[name] = (...args: any[]) => navigation.dispatch(method(...args));
|
|
}
|
|
|
|
return acc;
|
|
}, {}),
|
|
original: navigation,
|
|
addListener(type: EventName, callback: () => void) {
|
|
let unsubscribe: () => void;
|
|
|
|
switch (type) {
|
|
case 'willFocus':
|
|
unsubscribe = navigation.addListener('focus', callback);
|
|
break;
|
|
case 'willBlur':
|
|
unsubscribe = navigation.addListener('blur', callback);
|
|
break;
|
|
case 'didFocus': {
|
|
const listener = () => {
|
|
if (navigation.isFocused()) {
|
|
callback();
|
|
}
|
|
};
|
|
|
|
// @ts-ignore
|
|
unsubscribe = navigation.addListener('transitionEnd', listener);
|
|
focusSubscriptions.set(callback, unsubscribe);
|
|
break;
|
|
}
|
|
case 'didBlur': {
|
|
const listener = () => {
|
|
if (!navigation.isFocused()) {
|
|
callback();
|
|
}
|
|
};
|
|
|
|
// @ts-ignore
|
|
unsubscribe = navigation.addListener('transitionEnd', listener);
|
|
blurSubscriptions.set(callback, unsubscribe);
|
|
break;
|
|
}
|
|
case 'refocus': {
|
|
const listener = () => {
|
|
if (navigation.isFocused()) {
|
|
callback();
|
|
}
|
|
};
|
|
|
|
// @ts-ignore
|
|
unsubscribe = navigation.addListener('tabPress', listener);
|
|
refocusSubscriptions.set(callback, unsubscribe);
|
|
break;
|
|
}
|
|
default:
|
|
// @ts-ignore
|
|
unsubscribe = navigation.addListener(type, callback);
|
|
}
|
|
|
|
const subscription = () => unsubscribe();
|
|
|
|
subscription.remove = unsubscribe;
|
|
|
|
return subscription;
|
|
},
|
|
removeListener(type: EventName, callback: () => void) {
|
|
switch (type) {
|
|
case 'willFocus':
|
|
navigation.removeListener('focus', callback);
|
|
break;
|
|
case 'willBlur':
|
|
navigation.removeListener('blur', callback);
|
|
break;
|
|
case 'didFocus': {
|
|
const unsubscribe = focusSubscriptions.get(callback);
|
|
unsubscribe && unsubscribe();
|
|
break;
|
|
}
|
|
case 'didBlur': {
|
|
const unsubscribe = blurSubscriptions.get(callback);
|
|
unsubscribe && unsubscribe();
|
|
break;
|
|
}
|
|
case 'refocus': {
|
|
const unsubscribe = refocusSubscriptions.get(callback);
|
|
unsubscribe && unsubscribe();
|
|
break;
|
|
}
|
|
default:
|
|
// @ts-ignore
|
|
navigation.removeListener(type, callback);
|
|
}
|
|
},
|
|
state: {
|
|
...state,
|
|
// @ts-ignore
|
|
routeName: state.name,
|
|
get index() {
|
|
// @ts-ignore
|
|
if (state.index !== undefined) {
|
|
// @ts-ignore
|
|
return state.index;
|
|
}
|
|
|
|
console.warn(
|
|
"Accessing child navigation state for a route is not safe and won't work correctly."
|
|
);
|
|
|
|
// @ts-ignore
|
|
return state.state ? state.state.index : undefined;
|
|
},
|
|
get routes() {
|
|
// @ts-ignore
|
|
if (state.routes !== undefined) {
|
|
// @ts-ignore
|
|
return state.routes;
|
|
}
|
|
|
|
console.warn(
|
|
"Accessing child navigation state for a route is not safe and won't work correctly."
|
|
);
|
|
|
|
// @ts-ignore
|
|
return state.state ? state.state.routes : undefined;
|
|
},
|
|
},
|
|
getParam<T extends keyof ParamList>(
|
|
paramName: T,
|
|
defaultValue: ParamList[T]
|
|
): ParamList[T] {
|
|
// @ts-ignore
|
|
const params = state.params;
|
|
|
|
if (params && paramName in params) {
|
|
return params[paramName];
|
|
}
|
|
|
|
return defaultValue;
|
|
},
|
|
isFirstRouteInParent(): boolean {
|
|
const { routes } = navigation.dangerouslyGetState();
|
|
|
|
// @ts-ignore
|
|
return routes[0].key === state.key;
|
|
},
|
|
dangerouslyGetParent() {
|
|
const parent = navigation.dangerouslyGetParent();
|
|
|
|
if (parent) {
|
|
return createCompatNavigationProp(
|
|
parent,
|
|
navigation.dangerouslyGetState()
|
|
);
|
|
}
|
|
|
|
return undefined;
|
|
},
|
|
} as any;
|
|
}
|