[flow] Make StackRouter action creators optional and add DrawerRouter ones (#4257)

This will force all Flow users to do runtime assertions if they want to use these action creators. Open to any better solutions.
This commit is contained in:
Ashoat Tevosyan 2018-05-23 13:18:35 -04:00 committed by Brent Vatne
parent 72f17538c2
commit 046a9f8930
6 changed files with 61 additions and 35 deletions

View File

@ -16,6 +16,8 @@ import {
SafeAreaView, SafeAreaView,
withNavigation, withNavigation,
} from 'react-navigation'; } from 'react-navigation';
import invariant from 'invariant';
import SampleText from './SampleText'; import SampleText from './SampleText';
import { Button } from './commonComponents/ButtonWithMargin'; import { Button } from './commonComponents/ButtonWithMargin';
import { HeaderButtons } from './commonComponents/HeaderButtons'; import { HeaderButtons } from './commonComponents/HeaderButtons';
@ -48,11 +50,16 @@ const MyBackButtonWithNavigation = withNavigation(MyBackButton);
class MyNavScreen extends React.Component<MyNavScreenProps> { class MyNavScreen extends React.Component<MyNavScreenProps> {
render() { render() {
const { navigation, banner } = this.props; const { navigation, banner } = this.props;
const { push, replace, popToTop, pop, dismiss } = navigation;
invariant(
push && replace && popToTop && pop && dismiss,
'missing action creators for StackNavigator'
);
return ( return (
<SafeAreaView> <SafeAreaView>
<SampleText>{banner}</SampleText> <SampleText>{banner}</SampleText>
<Button <Button
onPress={() => navigation.push('Profile', { name: 'Jane' })} onPress={() => push('Profile', { name: 'Jane' })}
title="Push a profile screen" title="Push a profile screen"
/> />
<Button <Button
@ -60,13 +67,13 @@ class MyNavScreen extends React.Component<MyNavScreenProps> {
title="Navigate to a photos screen" title="Navigate to a photos screen"
/> />
<Button <Button
onPress={() => navigation.replace('Profile', { name: 'Lucy' })} onPress={() => replace('Profile', { name: 'Lucy' })}
title="Replace with profile" title="Replace with profile"
/> />
<Button onPress={() => navigation.popToTop()} title="Pop to top" /> <Button onPress={() => popToTop()} title="Pop to top" />
<Button onPress={() => navigation.pop()} title="Pop" /> <Button onPress={() => pop()} title="Pop" />
<Button onPress={() => navigation.goBack()} title="Go back" /> <Button onPress={() => navigation.goBack()} title="Go back" />
<Button onPress={() => navigation.dismiss()} title="Dismiss" /> <Button onPress={() => dismiss()} title="Dismiss" />
<StatusBar barStyle="default" /> <StatusBar barStyle="default" />
</SafeAreaView> </SafeAreaView>
); );

View File

@ -6,6 +6,8 @@ import type { NavigationScreenProp } from 'react-navigation';
import * as React from 'react'; import * as React from 'react';
import { ScrollView, StatusBar } from 'react-native'; import { ScrollView, StatusBar } from 'react-native';
import { createStackNavigator, SafeAreaView } from 'react-navigation'; import { createStackNavigator, SafeAreaView } from 'react-navigation';
import invariant from 'invariant';
import { Button } from './commonComponents/ButtonWithMargin'; import { Button } from './commonComponents/ButtonWithMargin';
type NavScreenProps = { type NavScreenProps = {
@ -19,15 +21,14 @@ class HomeScreen extends React.Component<NavScreenProps> {
render() { render() {
const { navigation } = this.props; const { navigation } = this.props;
const { push } = navigation;
invariant(push, 'missing `push` action creator for StackNavigator');
return ( return (
<SafeAreaView style={{ paddingTop: 30 }}> <SafeAreaView style={{ paddingTop: 30 }}>
<Button onPress={() => push('Other')} title="Push another screen" />
<Button <Button
onPress={() => navigation.push('Other')} onPress={() => push('ScreenWithNoHeader')}
title="Push another screen"
/>
<Button
onPress={() => navigation.push('ScreenWithNoHeader')}
title="Push screen with no header" title="Push screen with no header"
/> />
<Button onPress={() => navigation.goBack(null)} title="Go Home" /> <Button onPress={() => navigation.goBack(null)} title="Go Home" />
@ -44,18 +45,20 @@ class OtherScreen extends React.Component<NavScreenProps> {
render() { render() {
const { navigation } = this.props; const { navigation } = this.props;
const { push, pop } = navigation;
invariant(push && pop, 'missing action creators for StackNavigator');
return ( return (
<SafeAreaView style={{ paddingTop: 30 }}> <SafeAreaView style={{ paddingTop: 30 }}>
<Button <Button
onPress={() => navigation.push('ScreenWithLongTitle')} onPress={() => push('ScreenWithLongTitle')}
title="Push another screen" title="Push another screen"
/> />
<Button <Button
onPress={() => navigation.push('ScreenWithNoHeader')} onPress={() => push('ScreenWithNoHeader')}
title="Push screen with no header" title="Push screen with no header"
/> />
<Button onPress={() => navigation.pop()} title="Pop" /> <Button onPress={() => pop()} title="Pop" />
<Button onPress={() => navigation.goBack(null)} title="Go back" /> <Button onPress={() => navigation.goBack(null)} title="Go back" />
<StatusBar barStyle="default" /> <StatusBar barStyle="default" />
</SafeAreaView> </SafeAreaView>
@ -70,10 +73,12 @@ class ScreenWithLongTitle extends React.Component<NavScreenProps> {
render() { render() {
const { navigation } = this.props; const { navigation } = this.props;
const { pop } = navigation;
invariant(pop, 'missing `pop` action creator for StackNavigator');
return ( return (
<SafeAreaView style={{ paddingTop: 30 }}> <SafeAreaView style={{ paddingTop: 30 }}>
<Button onPress={() => navigation.pop()} title="Pop" /> <Button onPress={() => pop()} title="Pop" />
<Button onPress={() => navigation.goBack(null)} title="Go back" /> <Button onPress={() => navigation.goBack(null)} title="Go back" />
<StatusBar barStyle="default" /> <StatusBar barStyle="default" />
</SafeAreaView> </SafeAreaView>
@ -89,14 +94,13 @@ class ScreenWithNoHeader extends React.Component<NavScreenProps> {
render() { render() {
const { navigation } = this.props; const { navigation } = this.props;
const { push, pop } = navigation;
invariant(push && pop, 'missing action creators for StackNavigator');
return ( return (
<SafeAreaView style={{ paddingTop: 30 }}> <SafeAreaView style={{ paddingTop: 30 }}>
<Button <Button onPress={() => push('Other')} title="Push another screen" />
onPress={() => navigation.push('Other')} <Button onPress={() => pop()} title="Pop" />
title="Push another screen"
/>
<Button onPress={() => navigation.pop()} title="Pop" />
<Button onPress={() => navigation.goBack(null)} title="Go back" /> <Button onPress={() => navigation.goBack(null)} title="Go back" />
<StatusBar barStyle="default" /> <StatusBar barStyle="default" />
</SafeAreaView> </SafeAreaView>

View File

@ -19,6 +19,8 @@ import {
View, View,
} from 'react-native'; } from 'react-native';
import { Header, createStackNavigator } from 'react-navigation'; import { Header, createStackNavigator } from 'react-navigation';
import invariant from 'invariant';
import SampleText from './SampleText'; import SampleText from './SampleText';
import { Button } from './commonComponents/ButtonWithMargin'; import { Button } from './commonComponents/ButtonWithMargin';
import { HeaderButtons } from './commonComponents/HeaderButtons'; import { HeaderButtons } from './commonComponents/HeaderButtons';
@ -31,11 +33,16 @@ type MyNavScreenProps = {
class MyNavScreen extends React.Component<MyNavScreenProps> { class MyNavScreen extends React.Component<MyNavScreenProps> {
render() { render() {
const { navigation, banner } = this.props; const { navigation, banner } = this.props;
const { push, replace, popToTop, pop } = navigation;
invariant(
push && replace && popToTop && pop,
'missing action creators for StackNavigator'
);
return ( return (
<ScrollView style={{ flex: 1 }} {...this.getHeaderInset()}> <ScrollView style={{ flex: 1 }} {...this.getHeaderInset()}>
<SampleText>{banner}</SampleText> <SampleText>{banner}</SampleText>
<Button <Button
onPress={() => navigation.push('Profile', { name: 'Jane' })} onPress={() => push('Profile', { name: 'Jane' })}
title="Push a profile screen" title="Push a profile screen"
/> />
<Button <Button
@ -43,11 +50,11 @@ class MyNavScreen extends React.Component<MyNavScreenProps> {
title="Navigate to a photos screen" title="Navigate to a photos screen"
/> />
<Button <Button
onPress={() => navigation.replace('Profile', { name: 'Lucy' })} onPress={() => replace('Profile', { name: 'Lucy' })}
title="Replace with profile" title="Replace with profile"
/> />
<Button onPress={() => navigation.popToTop()} title="Pop to top" /> <Button onPress={() => popToTop()} title="Pop to top" />
<Button onPress={() => navigation.pop()} title="Pop" /> <Button onPress={() => pop()} title="Pop" />
<Button onPress={() => navigation.goBack(null)} title="Go back" /> <Button onPress={() => navigation.goBack(null)} title="Go back" />
<StatusBar barStyle="default" /> <StatusBar barStyle="default" />
</ScrollView> </ScrollView>

View File

@ -12,6 +12,7 @@
}, },
"dependencies": { "dependencies": {
"expo": "^26.0.0", "expo": "^26.0.0",
"invariant": "^2.2.4",
"react": "16.3.0-alpha.1", "react": "16.3.0-alpha.1",
"react-native": "^0.54.0", "react-native": "^0.54.0",
"react-native-iphone-x-helper": "^1.0.2", "react-native-iphone-x-helper": "^1.0.2",

View File

@ -3424,7 +3424,7 @@ instapromise@^2.0.7:
version "2.0.7" version "2.0.7"
resolved "https://registry.yarnpkg.com/instapromise/-/instapromise-2.0.7.tgz#85e66b31021194da11214c865127ef04ec30167a" resolved "https://registry.yarnpkg.com/instapromise/-/instapromise-2.0.7.tgz#85e66b31021194da11214c865127ef04ec30167a"
invariant@^2.0.0, invariant@^2.2.0, invariant@^2.2.2: invariant@^2.0.0, invariant@^2.2.0, invariant@^2.2.2, invariant@^2.2.4:
version "2.2.4" version "2.2.4"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
dependencies: dependencies:

View File

@ -489,8 +489,14 @@ declare module 'react-navigation' {
declare export type NavigationScreenProp<+S> = { declare export type NavigationScreenProp<+S> = {
+state: S, +state: S,
dispatch: NavigationDispatch, dispatch: NavigationDispatch,
addListener: (
eventName: string,
callback: NavigationEventCallback
) => NavigationEventSubscription,
getParam: (paramName: string, fallback?: any) => any,
isFocused: () => boolean,
// Shared action creators that exist for all routers
goBack: (routeKey?: ?string) => boolean, goBack: (routeKey?: ?string) => boolean,
dismiss: () => boolean,
navigate: ( navigate: (
routeName: routeName:
| string | string
@ -504,24 +510,25 @@ declare module 'react-navigation' {
action?: NavigationNavigateAction action?: NavigationNavigateAction
) => boolean, ) => boolean,
setParams: (newParams: NavigationParams) => boolean, setParams: (newParams: NavigationParams) => boolean,
getParam: (paramName: string, fallback?: any) => any, // StackRouter action creators
addListener: ( pop?: (n?: number, params?: { immediate?: boolean }) => boolean,
eventName: string, popToTop?: (params?: { immediate?: boolean }) => boolean,
callback: NavigationEventCallback push?: (
) => NavigationEventSubscription,
push: (
routeName: string, routeName: string,
params?: NavigationParams, params?: NavigationParams,
action?: NavigationNavigateAction action?: NavigationNavigateAction
) => boolean, ) => boolean,
replace: ( replace?: (
routeName: string, routeName: string,
params?: NavigationParams, params?: NavigationParams,
action?: NavigationNavigateAction action?: NavigationNavigateAction
) => boolean, ) => boolean,
pop: (n?: number, params?: { immediate?: boolean }) => boolean, reset?: (actions: NavigationAction[], index: number) => boolean,
popToTop: (params?: { immediate?: boolean }) => boolean, dismiss?: () => boolean,
isFocused: () => boolean, // DrawerRouter action creators
openDrawer?: () => boolean,
closeDrawer?: () => boolean,
toggleDrawer?: () => boolean,
}; };
declare export type NavigationNavigatorProps<O: {}, S: {}> = $Shape<{ declare export type NavigationNavigatorProps<O: {}, S: {}> = $Shape<{