typescript - Jak správně typovat useNavigation v React Navigation?
Snažím se napsat useNavigation z React Navigation. Chtěl bych být schopen předat pouze název trasy, ale dostanu chybu, pokud také nepředám vlastnosti pro tu trasu.
Podle dokumentace rozumím tomu, že implementace by měla vypadat nějak takto:
import { StackNavigationProp } from '@react-navigation/stack';
type StackParamList = {
Home: { foo: string, onBar: () => void }
About: AboutProps
}
type NavigationProps = StackNavigationProp<StackParamList>
const MyComponent = () => {
const navigation = useNavigation<NavigationProps>()
const handleOnNavigate = () => navigation.navigate('Home')
// ^ TypeError!
Na posledním řádku dostávám TypeError. Pokud přidám vlastnosti pro tuto trasu, chyba zmizí, ale neměl bych to muset dělat.
navigation.navigate('Home', { foo: 'hello', onBar: () => {} })
Při pohledu na deklaraci typu pro navigation.navigate (viz níže) to by nemělo být nutné, protože existuje přetížení pro pouhé předání názvu trasy jako jediného argumentu. Musím dělat něco špatně, protože to není přijato ... ale co, kde a proč?
Zde je CodeSandBox, který reprodukuje TypeError.
Typy React Navigation types.d.ts (odkaz)
navigate<RouteName extends keyof ParamList>(...args: undefined extends ParamList[RouteName]
? [screen: RouteName] | [screen: RouteName, params: ParamList[RouteName]]
: [screen: RouteName, params: ParamList[RouteName]])
: void;
Odpověď č. 1
Při používání Typescriptu by měla být celá navigace přísně typována. Naštěstí nemusíme psát obálky pro jejich implementace, protože nám jsou poskytovány obecné typy.
Nejjednodušší způsob, který jsem našel, jak přidat typy k hooku useNavigation, je následující:
Implementace typu
RootStackParamListmusí být upravena, pokud vaše trasa přijímá argumenty
// src/_app.tsx
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import HomeScreen from "./screens/home";
import AuthScreen from "./screens/auth";
export type ScreenNames = ["Home", "Auth"] // type these manually
export type RootStackParamList = Record<ScreenNames[number], undefined>;
export type StackNavigation = NavigationProp<RootStackParamList>;
const Stack = createNativeStackNavigator<RootStackParamList>();
export const App = () => {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Auth" component={AuthScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
// src/screens/home.tsx
import { useNavigation } from '@react-navigation/native';
import { type StackNavigation } from "../_app";
const HomeScreen = () => {
const { navigate } = useNavigation<StackNavigation>();
const handleOnNavigate = () => navigate("Home");
// ... the rest of the component code
}
export default HomeScreen;
Vezměte také na vědomí, že nepoužívají háček useNavigation pro stránky/obrazovky, ale spíše pro hluboko zanořené komponenty, protože navigátor je již předáván jako vlastnost komponentám předaným do Stack.Screen, což znamená, že můžete také použít navigátor HomeScreen následujícím způsobem:
// src/screens/home.tsx
import { type StackNavigation } from "../_app";
interface HomeScreenProps {
navigation: StackNavigation;
}
const HomeScreen: React.FC<HomeScreenProps> = ({ navigation }) => {
const handleOnNavigate = () => navigation.navigate("Home");
// ... the rest of the component code
}
export default HomeScreen;
Odpověď č. 2
import { useNavigation, ParamListBase, NavigationProp } from '@react-navigation/native';
const navigation: NavigationProp<ParamListBase> = useNavigation();
vše, co potřebujete udělat, je importovat požadovaný hook, prop a typ a jednoduše aplikovat, jak je ukázáno výše v příkladu. Na zdraví 👋
