diff --git a/packages/react-navigation/docs/api/navigators/Navigators.md b/packages/react-navigation/docs/api/navigators/Navigators.md
index 5ed0351f..3ae197b6 100644
--- a/packages/react-navigation/docs/api/navigators/Navigators.md
+++ b/packages/react-navigation/docs/api/navigators/Navigators.md
@@ -52,10 +52,6 @@ For the purpose of convenience, the built-in navigators have this ability becaus
Sometimes it is useful to know when navigation state managed by the top-level navigator changes. For this purpose, this function gets called every time with the previous state and the new state of the navigation.
-### `containerOptions`
+### `uriPrefix`
-These options can be used to configure a navigator when it is used at the top level.
-
-An error will be thrown if a navigator is configured with `containerOptions` and also receives a `navigation` prop, because in that case it would be unclear if the navigator should handle its own state.
-
-- `URIPrefix` - The prefix of the URIs that the app might handle. This will be used when handling a [deep link](/docs/guides/linking) to extract the path passed to the router.
\ No newline at end of file
+The prefix of the URIs that the app might handle. This will be used when handling a [deep link](/docs/guides/linking) to extract the path passed to the router.
diff --git a/packages/react-navigation/docs/guides/Deep-Linking.md b/packages/react-navigation/docs/guides/Deep-Linking.md
index 8e493281..286ca457 100644
--- a/packages/react-navigation/docs/guides/Deep-Linking.md
+++ b/packages/react-navigation/docs/guides/Deep-Linking.md
@@ -30,17 +30,15 @@ const SimpleApp = StackNavigator({
### URI Prefix
-Next, let's configure our navigation container to extract the path from the app's incoming URI. When configuring a top-level navigator, we can provide `containerOptions`:
+Next, let's configure our navigation container to extract the path from the app's incoming URI.
```js
-const SimpleApp = StackNavigator({
- ...
-}, {
- containerOptions: {
- // on Android, the URI prefix typically contains a host in addition to scheme
- URIPrefix: Platform.OS == 'android' ? 'mychat://mychat/' : 'mychat://',
- },
-});
+const SimpleApp = StackNavigator({...});
+
+// on Android, the URI prefix typically contains a host in addition to scheme
+const prefix = Platform.OS == 'android' ? 'mychat://mychat/' : 'mychat://';
+
+const MainApp = () => ;
```
## iOS
diff --git a/packages/react-navigation/src/TypeDefinition.js b/packages/react-navigation/src/TypeDefinition.js
index c0ba6585..782ea159 100644
--- a/packages/react-navigation/src/TypeDefinition.js
+++ b/packages/react-navigation/src/TypeDefinition.js
@@ -186,15 +186,6 @@ export type NavigationUriAction = {
uri: string,
};
-export type NavigationContainerOptions = {
- // This is used to extract the path from the URI passed to the app for a deep link
- URIPrefix?: string,
-};
-
-export type NavigationContainerConfig = {
- containerOptions?: NavigationContainerOptions,
-};
-
export type NavigationStackViewConfig = {
mode?: 'card' | 'modal',
headerMode?: HeaderMode,
@@ -301,9 +292,10 @@ export type NavigationScreenProp = {
setParams: (newParams: NavigationParams) => boolean,
};
-export type NavigationNavigatorProps = {
- navigation: NavigationProp,
+export type NavigationNavigatorProps = {
+ navigation: NavigationProp,
screenProps: *,
+ navigationOptions: *,
};
/**
diff --git a/packages/react-navigation/src/createNavigationContainer.js b/packages/react-navigation/src/createNavigationContainer.js
index 9b77cf00..38b6b7a8 100644
--- a/packages/react-navigation/src/createNavigationContainer.js
+++ b/packages/react-navigation/src/createNavigationContainer.js
@@ -12,12 +12,22 @@ import addNavigationHelpers from './addNavigationHelpers';
import type {
NavigationRoute,
NavigationAction,
- NavigationContainerOptions,
- NavigationProp,
NavigationState,
NavigationScreenProp,
+ NavigationNavigatorProps,
} from './TypeDefinition';
+type NavigationContainerProps = {
+ uriPrefix?: string,
+ onNavigationStateChange?: (NavigationState, NavigationState) => void,
+};
+
+type Props = NavigationContainerProps & NavigationNavigatorProps;
+
+type State = {
+ nav: ?NavigationState,
+};
+
/**
* Create an HOC that injects the navigation and manages the navigation state
* in case it's not passed from above.
@@ -25,35 +35,17 @@ import type {
* components.
*/
export default function createNavigationContainer(
- Component: ReactClass<*>,
- containerConfig?: NavigationContainerOptions
+ Component: ReactClass>,
+ containerOptions?: {},
) {
- type Props = {
- navigation: NavigationProp,
- onNavigationStateChange?: (NavigationState, NavigationState) => void,
- };
+ invariant(
+ typeof containerOptions === 'undefined',
+ 'containerOptions.URIPrefix has been removed. Pass the uriPrefix prop to the navigator instead',
+ );
- type State = {
- nav: ?NavigationState,
- };
-
- function urlToPathAndParams(url: string) {
- const params = {};
- const URIPrefix = containerConfig && containerConfig.URIPrefix;
- const delimiter = URIPrefix || '://';
- let path = url.split(delimiter)[1];
- if (!path) {
- path = url;
- }
- return {
- path,
- params,
- };
- }
-
- class NavigationContainer extends React.Component {
+ class NavigationContainer extends React.Component, State> {
state: State;
- props: Props;
+ props: Props;
subs: ?{
remove: () => void,
@@ -61,23 +53,11 @@ export default function createNavigationContainer(
static router = Component.router;
- _isStateful: () => boolean = () => {
- const hasNavProp = !!this.props.navigation;
- if (hasNavProp) {
- invariant(
- !containerConfig,
- 'This navigator has a container config AND a navigation prop, so it is ' +
- 'unclear if it should own its own state. Remove the containerConfig ' +
- 'if the navigator should get its state from the navigation prop. If the ' +
- 'navigator should maintain its own state, do not pass a navigation prop.'
- );
- return false;
- }
- return true;
- }
-
- constructor(props: Props) {
+ constructor(props: Props) {
super(props);
+
+ this._validateProps(props);
+
this.state = {
nav: this._isStateful()
? Component.router.getStateForAction(NavigationActions.init())
@@ -85,50 +65,46 @@ export default function createNavigationContainer(
};
}
- componentDidMount() {
+ _isStateful(): boolean {
+ return !this.props.navigation;
+ }
+
+ _validateProps(props: Props) {
if (this._isStateful()) {
- this.subs = BackAndroid.addEventListener('backPress', () =>
- this.dispatch(NavigationActions.back())
- );
- Linking.addEventListener('url', this._handleOpenURL);
- Linking.getInitialURL().then((url: string) => {
- if (url) {
- console.log('Handling URL:', url);
- const parsedUrl = urlToPathAndParams(url);
- if (parsedUrl) {
- const { path, params } = parsedUrl;
- const action = Component.router.getActionForPathAndParams(path, params);
- if (action) {
- this.dispatch(action);
- }
- }
- }
- });
+ return;
}
+
+ const {
+ navigation, screenProps, navigationOptions, onNavigationStateChange,
+ ...containerProps
+ } = props;
+
+ const keys = Object.keys(containerProps);
+
+ invariant(
+ keys.length === 0,
+ 'This navigator has both navigation and container props, so it is ' +
+ `unclear if it should own its own state. Remove props: "${keys.join(', ')}" ` +
+ 'if the navigator should get its state from the navigation prop. If the ' +
+ 'navigator should maintain its own state, do not pass a navigation prop.',
+ );
}
- componentDidUpdate(prevProps: Props, prevState: State) {
- const [prevNavigationState, navigationState] = this._isStateful()
- ? [prevState.nav, this.state.nav]
- : [prevProps.navigation.state, this.props.navigation.state];
-
- if (
- prevNavigationState !== navigationState
- && typeof this.props.onNavigationStateChange === 'function'
- ) {
- // $FlowFixMe state is always defined, either this.state or props
- this.props.onNavigationStateChange(prevNavigationState, navigationState);
+ _urlToPathAndParams(url: string) {
+ const params = {};
+ const delimiter = this.props.uriPrefix || '://';
+ let path = url.split(delimiter)[1];
+ if (!path) {
+ path = url;
}
+ return {
+ path,
+ params,
+ };
}
- componentWillUnmount() {
- Linking.removeEventListener('url', this._handleOpenURL);
- this.subs && this.subs.remove();
- }
-
- _handleOpenURL = ({ url }: { url: string }) => {
- console.log('Handling URL:', url);
- const parsedUrl = urlToPathAndParams(url);
+ _handleOpenURL = (url: string) => {
+ const parsedUrl = this._urlToPathAndParams(url);
if (parsedUrl) {
const { path, params } = parsedUrl;
const action = Component.router.getActionForPathAndParams(path, params);
@@ -138,13 +114,51 @@ export default function createNavigationContainer(
}
};
+ componentWillReceiveProps(nextProps: *) {
+ this._validateProps(nextProps);
+ }
+
+ componentDidMount() {
+ if (!this._isStateful()) {
+ return;
+ }
+
+ this.subs = BackAndroid.addEventListener(
+ 'backPress',
+ () => this.dispatch(NavigationActions.back()),
+ );
+
+ Linking.addEventListener('url', ({ url }: { url: string }) => {
+ this._handleOpenURL(url);
+ });
+
+ Linking.getInitialURL().then((url: string) => url && this._handleOpenURL(url));
+ }
+
+ componentDidUpdate(prevProps: Props, prevState: State) {
+ const [prevNavigationState, navigationState] = this._isStateful()
+ ? [prevState.nav, this.state.nav]
+ : [prevProps.navigation.state, this.props.navigation.state];
+
+ if (
+ prevNavigationState !== navigationState
+ && typeof this.props.onNavigationStateChange === 'function'
+ ) {
+ this.props.onNavigationStateChange(prevNavigationState, navigationState);
+ }
+ }
+
+ componentWillUnmount() {
+ Linking.removeEventListener('url', this._handleOpenURL);
+ this.subs && this.subs.remove();
+ }
+
dispatch = (action: NavigationAction) => {
const { state } = this;
if (!this._isStateful()) {
return false;
}
const nav = Component.router.getStateForAction(action, state.nav);
-
if (nav && nav !== state.nav) {
if (console.group) {
console.group('Navigation Dispatch: ');
@@ -168,7 +182,7 @@ export default function createNavigationContainer(
if (this._isStateful()) {
if (!this._navigation || this._navigation.state !== this.state.nav) {
this._navigation = addNavigationHelpers({
- dispatch: this.dispatch.bind(this),
+ dispatch: this.dispatch,
state: this.state.nav,
});
}
diff --git a/packages/react-navigation/src/navigators/DrawerNavigator.js b/packages/react-navigation/src/navigators/DrawerNavigator.js
index b38bc920..e85bfa1b 100644
--- a/packages/react-navigation/src/navigators/DrawerNavigator.js
+++ b/packages/react-navigation/src/navigators/DrawerNavigator.js
@@ -15,13 +15,12 @@ import NavigatorTypes from './NavigatorTypes';
import type { DrawerViewConfig } from '../views/Drawer/DrawerView';
import type {
- NavigationContainerConfig,
NavigationRouteConfigMap,
NavigationTabRouterConfig,
} from '../TypeDefinition';
export type DrawerNavigatorConfig =
- & NavigationContainerConfig
+ & { containerConfig?: void }
& NavigationTabRouterConfig
& DrawerViewConfig;
@@ -48,11 +47,12 @@ const DrawerNavigator = (
drawerPosition,
...tabsConfig
} = mergedConfig;
+
const contentRouter = TabRouter(routeConfigs, tabsConfig);
const drawerRouter = TabRouter({
DrawerClose: {
- screen: createNavigator(contentRouter, routeConfigs, config, NavigatorTypes.DRAWER)((props: *) =>
-
+ screen: createNavigator(contentRouter, routeConfigs, config, NavigatorTypes.DRAWER)(
+ (props: *) => ,
),
},
DrawerOpen: {
@@ -61,15 +61,20 @@ const DrawerNavigator = (
}, {
initialRouteName: 'DrawerClose',
});
- return createNavigationContainer(createNavigator(drawerRouter, routeConfigs, config, NavigatorTypes.DRAWER)((props: *) =>
-
- ), containerConfig);
+
+ const navigator = createNavigator(drawerRouter, routeConfigs, config, NavigatorTypes.DRAWER)(
+ (props: *) => (
+
+ ),
+ );
+
+ return createNavigationContainer(navigator, containerConfig);
};
export default DrawerNavigator;
diff --git a/packages/react-navigation/src/navigators/StackNavigator.js b/packages/react-navigation/src/navigators/StackNavigator.js
index a13602af..6a38334a 100644
--- a/packages/react-navigation/src/navigators/StackNavigator.js
+++ b/packages/react-navigation/src/navigators/StackNavigator.js
@@ -8,20 +8,21 @@ import StackRouter from '../routers/StackRouter';
import NavigatorTypes from './NavigatorTypes';
import type {
- NavigationContainerConfig,
NavigationStackRouterConfig,
NavigationStackViewConfig,
NavigationRouteConfigMap,
} from '../TypeDefinition';
export type StackNavigatorConfig =
- & NavigationContainerConfig
+ & { containerOptions?: void }
& NavigationStackViewConfig
& NavigationStackRouterConfig;
-export default (routeConfigMap: NavigationRouteConfigMap, stackConfig: StackNavigatorConfig = {}) => {
+export default (
+ routeConfigMap: NavigationRouteConfigMap,
+ stackConfig: StackNavigatorConfig = {},
+) => {
const {
- containerOptions,
initialRouteName,
initialRouteParams,
paths,
@@ -39,16 +40,21 @@ export default (routeConfigMap: NavigationRouteConfigMap, stackConfig: StackNavi
paths,
navigationOptions,
};
+
const router = StackRouter(routeConfigMap, stackRouterConfig);
- return createNavigationContainer(createNavigator(router, routeConfigMap, stackConfig, NavigatorTypes.STACK)(props => (
-
- )), containerOptions);
+ const navigator = createNavigator(router, routeConfigMap, stackConfig, NavigatorTypes.STACK)(
+ (props: *) => (
+
+ ),
+ );
+
+ return createNavigationContainer(navigator, stackConfig.containerOptions);
};
diff --git a/packages/react-navigation/src/navigators/TabNavigator.js b/packages/react-navigation/src/navigators/TabNavigator.js
index a45188f9..a18d23fe 100644
--- a/packages/react-navigation/src/navigators/TabNavigator.js
+++ b/packages/react-navigation/src/navigators/TabNavigator.js
@@ -12,24 +12,22 @@ import NavigatorTypes from './NavigatorTypes';
import type { TabViewConfig } from '../views/TabView/TabView';
import type {
- NavigationContainerConfig,
NavigationRouteConfigMap,
NavigationTabRouterConfig,
} from '../TypeDefinition';
export type TabNavigatorConfig =
+ & { containerOptions?: void }
& NavigationTabRouterConfig
- & TabViewConfig
- & NavigationContainerConfig;
+ & TabViewConfig;
const TabNavigator = (
routeConfigs: NavigationRouteConfigMap,
- config: TabNavigatorConfig = {}
+ config: TabNavigatorConfig = {},
) => {
// Use the look native to the platform by default
const mergedConfig = { ...TabNavigator.Presets.Default, ...config };
const {
- containerOptions,
tabBarComponent,
tabBarPosition,
tabBarOptions,
@@ -38,18 +36,23 @@ const TabNavigator = (
lazyLoad,
...tabsConfig
} = mergedConfig;
+
const router = TabRouter(routeConfigs, tabsConfig);
- return createNavigationContainer(createNavigator(router, routeConfigs, config, NavigatorTypes.STACK)((props: *) =>
-
- ), containerOptions);
+ const navigator = createNavigator(router, routeConfigs, config, NavigatorTypes.STACK)(
+ (props: *) => (
+
+ ),
+ );
+
+ return createNavigationContainer(navigator, tabsConfig.containerOptions);
};
const Presets = {
diff --git a/packages/react-navigation/src/navigators/createNavigator.js b/packages/react-navigation/src/navigators/createNavigator.js
index 5d3fe82f..3b173a93 100644
--- a/packages/react-navigation/src/navigators/createNavigator.js
+++ b/packages/react-navigation/src/navigators/createNavigator.js
@@ -11,7 +11,7 @@ import type {
} from '../TypeDefinition';
import type {
- NavigatorType
+ NavigatorType,
} from './NavigatorTypes';
/**
@@ -20,7 +20,7 @@ import type {
const createNavigator = (router: NavigationRouter<*, *, *>, routeConfigs: NavigationRouteConfigMap, navigatorConfig: any, navigatorType: NavigatorType) =>
(View: NavigationNavigator<*, *, *, *>) => {
class Navigator extends React.Component {
- props: NavigationNavigatorProps;
+ props: NavigationNavigatorProps<*>;
static router = router;
diff --git a/packages/react-navigation/src/views/SceneView.js b/packages/react-navigation/src/views/SceneView.js
index a8e16077..e15d6770 100644
--- a/packages/react-navigation/src/views/SceneView.js
+++ b/packages/react-navigation/src/views/SceneView.js
@@ -7,13 +7,14 @@ import type {
NavigationScreenProp,
NavigationRoute,
NavigationAction,
+ NavigationNavigatorProps,
} from '../TypeDefinition';
type Props = {
screenProps?: {};
navigation: NavigationScreenProp;
navigationOptions: *,
- component: ReactClass<*>;
+ component: ReactClass>;
};
export default class SceneView extends PureComponent {