Initial stab at NavigationExperimental tutorial
Summary: Add a NavigationExperimental section to the Navigation guide. ![navexp](https://cloud.githubusercontent.com/assets/165856/16355280/120e7d38-3a67-11e6-9567-51c19c249fdf.png) Closes https://github.com/facebook/react-native/pull/8431 Differential Revision: D3493158 Pulled By: JoelMarcey fbshipit-source-id: 5e9646c3abf97f5cce6f5ba5b3d10853aa84ee8e
This commit is contained in:
parent
6b2a49e73e
commit
3085b35e63
|
@ -93,10 +93,17 @@ type Event = Object;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `NavigatorIOS` is a wrapper around UIKit navigation, enabling you to
|
* `NavigatorIOS` is a wrapper around
|
||||||
* implement back-swipe functionality in your iOS app. Take a look at
|
* [`UINavigationController`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UINavigationController_Class/),
|
||||||
|
* enabling you to implement a navigation stack. It works exactly the same as it
|
||||||
|
* would on a native app using `UINavigationController`, providing the same
|
||||||
|
* animations and behavior from UIKIt.
|
||||||
|
*
|
||||||
|
* As the name implies, it is only available on iOS. Take a look at
|
||||||
* [`Navigator`](/docs/navigator.html) for a similar solution for your
|
* [`Navigator`](/docs/navigator.html) for a similar solution for your
|
||||||
* cross-platform needs.
|
* cross-platform needs, or check out
|
||||||
|
* [react-native-navigation](https://github.com/wix/react-native-navigation), a
|
||||||
|
* component that aims to provide native navigation on both iOS and Android.
|
||||||
*
|
*
|
||||||
* To set up the navigator, provide the `initialRoute` prop with a route
|
* To set up the navigator, provide the `initialRoute` prop with a route
|
||||||
* object. A route object is used to describe each scene that your app
|
* object. A route object is used to describe each scene that your app
|
||||||
|
@ -106,13 +113,13 @@ type Event = Object;
|
||||||
* import React, { Component } from 'react';
|
* import React, { Component } from 'react';
|
||||||
* import { NavigatorIOS, Text } from 'react-native';
|
* import { NavigatorIOS, Text } from 'react-native';
|
||||||
*
|
*
|
||||||
* class NavvyIOS extends Component {
|
* export default class NavigatorIOSApp extends Component {
|
||||||
* render() {
|
* render() {
|
||||||
* return (
|
* return (
|
||||||
* <NavigatorIOS
|
* <NavigatorIOS
|
||||||
* initialRoute={{
|
* initialRoute={{
|
||||||
* component: MyView,
|
* component: MyScene,
|
||||||
* title: 'My View Title',
|
* title: 'My Initial Scene',
|
||||||
* }}
|
* }}
|
||||||
* style={{flex: 1}}
|
* style={{flex: 1}}
|
||||||
* />
|
* />
|
||||||
|
@ -120,39 +127,54 @@ type Event = Object;
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* class MyView extends Component {
|
* class MyScene extends Component {
|
||||||
|
* static propTypes = {
|
||||||
|
* title: PropTypes.string.isRequired,
|
||||||
|
* navigator: PropTypes.object.isRequired,
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* constructor(props, context) {
|
||||||
|
* super(props, context);
|
||||||
|
* this._onForward = this._onForward.bind(this);
|
||||||
|
* this._onBack = this._onBack.bind(this);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* _onForward() {
|
||||||
|
* this.props.navigator.push({
|
||||||
|
* title: 'Scene ' + nextIndex,
|
||||||
|
* });
|
||||||
|
* }
|
||||||
|
*
|
||||||
* render() {
|
* render() {
|
||||||
* return(
|
* return (
|
||||||
* <Text style={{marginTop: 200, alignSelf: 'center'}}>
|
* <View>
|
||||||
* See you on the other nav!
|
* <Text>Current Scene: { this.props.title }</Text>
|
||||||
* </Text>
|
* <TouchableHighlight onPress={this._onForward}>
|
||||||
* );
|
* <Text>Tap me to load the next scene</Text>
|
||||||
|
* </TouchableHighlight>
|
||||||
|
* </View>
|
||||||
|
* )
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* In this code, the navigator sets its title and renders `MyView`. The
|
* In this code, the navigator renders the component specified in initialRoute,
|
||||||
* component specified in `initialRoute` will receive a `route` prop and a
|
* which in this case is `MyScene`. This component will receive a `route` prop
|
||||||
* `navigator` prop representing the navigator.
|
* and a `navigator` prop representing the navigator. The navigator's navigation
|
||||||
|
* bar will render the title for the current scene, "My Initial Scene".
|
||||||
*
|
*
|
||||||
* You can optionally pass in a `passProps` property to your `initialRoute`.
|
* You can optionally pass in a `passProps` property to your `initialRoute`.
|
||||||
* `NavigatorIOS` passes this in as props to the rendered component:
|
* `NavigatorIOS` passes this in as props to the rendered component:
|
||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
* initialRoute={{
|
* initialRoute={{
|
||||||
* component: MyView,
|
* component: MyScene,
|
||||||
* title: 'Foo This',
|
* title: 'My Initial Scene',
|
||||||
* passProps: { myProp: 'foo' }
|
* passProps: { myProp: 'foo' }
|
||||||
* }}
|
* }}
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* You can then access the props passed in:
|
* You can then access the props passed in via `{this.props.myProp}`.
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* <Text style={{marginTop: 200, alignSelf: 'center'}}>
|
|
||||||
* See you on the other nav {this.props.myProp}!
|
|
||||||
* </Text>
|
|
||||||
* ```
|
|
||||||
*
|
*
|
||||||
* #### Handling Navigation
|
* #### Handling Navigation
|
||||||
*
|
*
|
||||||
|
@ -254,6 +276,7 @@ type Event = Object;
|
||||||
*
|
*
|
||||||
* In the example above the navigation bar color is changed when the new route
|
* In the example above the navigation bar color is changed when the new route
|
||||||
* is pushed.
|
* is pushed.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
var NavigatorIOS = React.createClass({
|
var NavigatorIOS = React.createClass({
|
||||||
|
|
||||||
|
|
|
@ -130,9 +130,10 @@ var GESTURE_ACTIONS = [
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `Navigator` handles the transition between different scenes in your app.
|
* `Navigator` handles the transition between different scenes in your app.
|
||||||
* You should consider using this component instead of `NavigatorIOS` if you're
|
* It is implemented in JavaScript and is available on both iOS and Android. If
|
||||||
* building a cross-platform app. `Navigator` is implemented in JavaScript
|
* you are targeting iOS only, you may also want to consider using
|
||||||
* whereas `NavigatorIOS` is a wrapper around `UINavigationController`.
|
* [`NavigatorIOS`](docs/navigatorios.html) as it leverages native UIKit
|
||||||
|
* navigation.
|
||||||
*
|
*
|
||||||
* To set up the `Navigator` you provide one or more objects called routes,
|
* To set up the `Navigator` you provide one or more objects called routes,
|
||||||
* to identify each scene. You also provide a `renderScene` function that
|
* to identify each scene. You also provide a `renderScene` function that
|
||||||
|
@ -142,13 +143,13 @@ var GESTURE_ACTIONS = [
|
||||||
* import React, { Component } from 'react';
|
* import React, { Component } from 'react';
|
||||||
* import { Text, Navigator } from 'react-native';
|
* import { Text, Navigator } from 'react-native';
|
||||||
*
|
*
|
||||||
* class NavAllDay extends Component {
|
* export default class NavAllDay extends Component {
|
||||||
* render() {
|
* render() {
|
||||||
* return (
|
* return (
|
||||||
* <Navigator
|
* <Navigator
|
||||||
* initialRoute={{name: 'Awesome Scene'}}
|
* initialRoute={{ title: 'Awesome Scene', index: 0 }}
|
||||||
* renderScene={(route, navigator) =>
|
* renderScene={(route, navigator) =>
|
||||||
* <Text>Hello {route.name}!</Text>
|
* <Text>Hello {route.title}!</Text>
|
||||||
* }
|
* }
|
||||||
* style={{padding: 100}}
|
* style={{padding: 100}}
|
||||||
* />
|
* />
|
||||||
|
@ -158,8 +159,8 @@ var GESTURE_ACTIONS = [
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* In the above example, `initialRoute` is used to specify the first route. It
|
* In the above example, `initialRoute` is used to specify the first route. It
|
||||||
* contains a `name` property that identifies the route. The `renderScene`
|
* contains a `title` property that identifies the route. The `renderScene`
|
||||||
* prop returns a function that displays text based on the route's name.
|
* prop returns a function that displays text based on the route's title.
|
||||||
*
|
*
|
||||||
* ### Additional Scenes
|
* ### Additional Scenes
|
||||||
*
|
*
|
||||||
|
@ -169,8 +170,8 @@ var GESTURE_ACTIONS = [
|
||||||
* ```
|
* ```
|
||||||
* render() {
|
* render() {
|
||||||
* const routes = [
|
* const routes = [
|
||||||
* {name: 'First Scene', index: 0},
|
* {title: 'First Scene', index: 0},
|
||||||
* {name: 'Second Scene', index: 1},
|
* {title: 'Second Scene', index: 1},
|
||||||
* ];
|
* ];
|
||||||
* return (
|
* return (
|
||||||
* <Navigator
|
* <Navigator
|
||||||
|
@ -184,7 +185,7 @@ var GESTURE_ACTIONS = [
|
||||||
* navigator.pop();
|
* navigator.pop();
|
||||||
* }
|
* }
|
||||||
* }}>
|
* }}>
|
||||||
* <Text>Hello {route.name}!</Text>
|
* <Text>Hello {route.title}!</Text>
|
||||||
* </TouchableHighlight>
|
* </TouchableHighlight>
|
||||||
* }
|
* }
|
||||||
* style={{padding: 100}}
|
* style={{padding: 100}}
|
||||||
|
@ -256,6 +257,12 @@ var GESTURE_ACTIONS = [
|
||||||
* This sets up a left navigator bar button that's visible on scenes after the
|
* This sets up a left navigator bar button that's visible on scenes after the
|
||||||
* the first one. When the button is tapped the navigator is popped.
|
* the first one. When the button is tapped the navigator is popped.
|
||||||
*
|
*
|
||||||
|
* Another type of navigation bar, with breadcrumbs, is provided by
|
||||||
|
* `Navigator.BreadcrumbNavigationBar`. You can also provide your own navigation
|
||||||
|
* bar by passing it through the `navigationBar` prop. See the
|
||||||
|
* [UIExplorer](https://github.com/facebook/react-native/tree/master/Examples/UIExplorer)
|
||||||
|
* demo to try out both built-in navigation bars out and see how to use them.
|
||||||
|
*
|
||||||
* ### Scene Transitions
|
* ### Scene Transitions
|
||||||
*
|
*
|
||||||
* To change the animation or gesture properties of the scene, provide a
|
* To change the animation or gesture properties of the scene, provide a
|
||||||
|
|
|
@ -7,30 +7,66 @@ permalink: docs/navigator-comparison.html
|
||||||
next: performance
|
next: performance
|
||||||
---
|
---
|
||||||
|
|
||||||
Mobile apps rarely consist of just one screen or scene. As soon as you add a second scene to your app, you will have to take into consideration how the user will navigate from one scene to the other.
|
Mobile apps rarely consist of just one scene (another word for screen). As soon as you add a second scene to your app, you will have to take into consideration how the user will navigate from one scene to the other.
|
||||||
|
|
||||||
Navigators in React Native allow you to push and pop scenes in a master/detail stack, or to pop up modal scenes. Navigators handle the transitions between scenes, and also maintain the navigational state of your application.
|
You can use navigators to transition between multiple scenes. These transitions can be typical side-to-side animations down a master/detail stack, or vertical modal popups.
|
||||||
|
|
||||||
If you are just getting started with React Native, you will probably want to start with the `Navigator` component.
|
|
||||||
|
|
||||||
## Navigator
|
## Navigator
|
||||||
|
|
||||||
`Navigator` is a cross-platform implementation of a navigation stack, so it works on both iOS and Android. It is easy to customize and includes simple navigation bars.
|
React Native has several built-in navigation components, but for your first app you will probably want to use `Navigator`. It provides a JavaScript implementation of a navigation stack, so it works on both iOS and Android and is easy to customize.
|
||||||
|
|
||||||
|
![](img/NavigationStack-Navigator.gif)
|
||||||
|
|
||||||
|
Something you will encounter a lot when dealing with navigation is the concept of routes. A route is an object that contains information about a scene. It is used to provide all the context that the navigator's `renderScene` function needs to render a scene. A basic `Navigator` implementation may look like this:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
<Navigator
|
<Navigator
|
||||||
initialRoute={{ title: 'My Initial Scene', index: 0}}
|
initialRoute={{ title: 'My Initial Scene', index: 0 }}
|
||||||
renderScene={(route, navigator) => {
|
renderScene={(route, navigator) => {
|
||||||
// We'll get to this function soon.
|
<MyScene title={route.title} />
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
```
|
```
|
||||||
|
|
||||||
Something you will encounter a lot when dealing with navigation is the concept of routes. A route is an object that contains information about a scene. It is used to provide all the context the `renderScene` function needs to render a scene.
|
The above example will display a single scene, but in order to push a new scene onto screen you will need to learn about `push` and `pop`. These two methods are provided by the `navigator` object that is passed to your `renderScene` function. They can be used, as you may have realized, to push and pop routes into your navigation stack.
|
||||||
|
|
||||||
The `push` and `pop` functions provided by Navigator can be used to push and pop routes into the navigation stack. A more complete example that demonstrates the pushing and popping of routes could therefore look something like this:
|
A more complete example that demonstrates the pushing and popping of routes could therefore look something like this:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
import { Navigator, Text, TouchableHighlight, View } from 'react-native';
|
||||||
|
|
||||||
|
export default class SimpleNavigationApp extends Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Navigator
|
||||||
|
initialRoute={{ title: 'My Initial Scene', index: 0 }}
|
||||||
|
renderScene={(route, navigator) =>
|
||||||
|
<MyScene
|
||||||
|
title={route.title}
|
||||||
|
|
||||||
|
// Function to call when a new scene should be displayed
|
||||||
|
onForward={ () => {
|
||||||
|
const nextIndex = route.index + 1;
|
||||||
|
navigator.push({
|
||||||
|
title: 'Scene ' + nextIndex,
|
||||||
|
index: nextIndex,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
|
||||||
|
// Function to call to go back to the previous scene
|
||||||
|
onBack={() => {
|
||||||
|
if (route.index > 0) {
|
||||||
|
navigator.pop();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class MyScene extends Component {
|
class MyScene extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.string.isRequired,
|
||||||
|
@ -51,42 +87,19 @@ class MyScene extends Component {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SimpleNavigationApp extends Component {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<Navigator
|
|
||||||
initialRoute={{ title: 'My Initial Scene', index: 0 }}
|
|
||||||
renderScene={(route, navigator) =>
|
|
||||||
<MyScene
|
|
||||||
title={route.title}
|
|
||||||
onForward={ () => {
|
|
||||||
const nextIndex = route.index + 1;
|
|
||||||
navigator.push({
|
|
||||||
title: 'Scene ' + nextIndex,
|
|
||||||
index: nextIndex,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
onBack={() => {
|
|
||||||
if (route.index > 0) {
|
|
||||||
navigator.pop();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
In this example, the `MyScene` component is passed the title of the current route via the `title` prop. It displays two tappable components that call the `onForward` and `onBack` functions passed through its props, which in turn will call `navigator.push()` and `navigator.pop()` as needed.
|
In this example, the `MyScene` component is passed the title of the current route via the `title` prop. It displays two tappable components that call the `onForward` and `onBack` functions passed through its props, which in turn will call `navigator.push()` and `navigator.pop()` as needed.
|
||||||
|
|
||||||
While this is a very basic example, it can easily be adapted to render an entirely different component based on the route that is passed to the `renderScene` function. Navigator will push new scenes from the right by default, and you can control this behavior by using the `configureScene` function. Check out the [Navigator API reference](docs/navigator.html) to learn more.
|
While this is a very basic example, it can easily be adapted to render an entirely different component based on the route that is passed to the `renderScene` function. Navigator will push new scenes from the right by default, and you can control this behavior by using the `configureScene` function. You can also configure a navigation bar through the `navigationBar` prop.
|
||||||
|
|
||||||
|
Check out the [Navigator API reference](docs/navigator.html) for more code samples.
|
||||||
|
|
||||||
## NavigatorIOS
|
## NavigatorIOS
|
||||||
|
|
||||||
If you are targeting iOS only, you may also want to consider using `NavigatorIOS`. It looks and feels just like `UINavigationController`, because it is actually built on top of it.
|
If you are targeting iOS only, you may also want to consider using [NavigatorIOS](docs/navigatorios.html). It looks and feels just like [`UINavigationController`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UINavigationController_Class/), because it is actually built on top of it.
|
||||||
|
|
||||||
|
![](img/NavigationStack-NavigatorIOS.gif)
|
||||||
|
|
||||||
```js
|
```js
|
||||||
<NavigatorIOS
|
<NavigatorIOS
|
||||||
|
@ -98,11 +111,30 @@ If you are targeting iOS only, you may also want to consider using `NavigatorIOS
|
||||||
/>
|
/>
|
||||||
```
|
```
|
||||||
|
|
||||||
Just like Navigator, it it uses routes to represent scenes, with some important differences. The actual component that will be rendered can be specified using the `component` key in the route, and any props that should be passed to this component can be specified in `passProps`. A navigator object is automatically passed as a prop to the component, allowing you to call `push` and `pop` as needed.
|
Just like Navigator, NavigatorIOS uses routes to represent scenes, with some important differences. The actual component that will be rendered can be specified using the `component` key in the route, and any props that should be passed to this component can be specified in `passProps`. A "navigator" object is automatically passed as a prop to the component, allowing you to call `push` and `pop` as needed.
|
||||||
|
|
||||||
|
As NavigatorIOS leverages native UIKit navigation, it will automatically render a navigation bar with a back button and title.
|
||||||
|
|
||||||
Check out the [NavigatorIOS reference docs](docs/navigatorios.html) to learn more about this component.
|
Check out the [NavigatorIOS reference docs](docs/navigatorios.html) to learn more about this component.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
import { NavigatorIOS, Text, TouchableHighlight, View } from 'react-native';
|
||||||
|
|
||||||
|
export default class NavigatorIOSApp extends Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<NavigatorIOS
|
||||||
|
initialRoute={{
|
||||||
|
component: MyScene,
|
||||||
|
title: 'My Initial Scene',
|
||||||
|
}}
|
||||||
|
style={{flex: 1}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class MyScene extends Component {
|
class MyScene extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.string.isRequired,
|
||||||
|
@ -112,7 +144,6 @@ class MyScene extends Component {
|
||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this._onForward = this._onForward.bind(this);
|
this._onForward = this._onForward.bind(this);
|
||||||
this._onBack = this._onBack.bind(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_onForward() {
|
_onForward() {
|
||||||
|
@ -121,10 +152,6 @@ class MyScene extends Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_onBack() {
|
|
||||||
this.props.navigator.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
|
@ -132,34 +159,262 @@ class MyScene extends Component {
|
||||||
<TouchableHighlight onPress={this._onForward}>
|
<TouchableHighlight onPress={this._onForward}>
|
||||||
<Text>Tap me to load the next scene</Text>
|
<Text>Tap me to load the next scene</Text>
|
||||||
</TouchableHighlight>
|
</TouchableHighlight>
|
||||||
<TouchableHighlight onPress={this._onBack}>
|
|
||||||
<Text>Tap me to go back</Text>
|
|
||||||
</TouchableHighlight>
|
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NavigatorIOSApp extends Component {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<NavigatorIOS
|
|
||||||
initialRoute={{
|
|
||||||
component: MyScene,
|
|
||||||
title: 'My Initial Scene',
|
|
||||||
index: 0
|
|
||||||
}}
|
|
||||||
renderScene={ (route, navigator) =>
|
|
||||||
<MyScene title={route.title} />
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
> You may also want to check out [react-native-navigation](https://github.com/wix/react-native-navigation), a component that aims to provide native navigation on both iOS and Android.
|
> You may also want to check out [react-native-navigation](https://github.com/wix/react-native-navigation), a component that aims to provide native navigation on both iOS and Android.
|
||||||
|
|
||||||
## Navigation (Experimental)
|
## NavigationExperimental
|
||||||
|
|
||||||
If you are looking for a more powerful navigation API, check out [NavigationExperimental](https://github.com/facebook/react-native/tree/master/Examples/UIExplorer/NavigationExperimental). It provides greater customization over your transitions, uses single-directional data flow using reducers to manipulate state at a top-level object, and offloads transition animations to the GPU.
|
Navigator and NavigatorIOS are both stateful components. If your app has multiple of these, it can become tricky to coordinate navigation transitions between them. NavigationExperimental provides a different approach to navigation, allowing any view to act as a navigation view and using reducers to manipulate state at a top-level object. It is bleeding edge as the name implies, but you might want to check it out if you are craving greater control over your app's navigation.
|
||||||
|
|
||||||
|
```js
|
||||||
|
<NavigationCardStack
|
||||||
|
onNavigateBack={onPopRouteFunc}
|
||||||
|
navigationState={myNavigationState}
|
||||||
|
renderScene={renderSceneFun}
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
You can import NavigationExperimental like any other component in React Native. Once you have that, you can deconstruct any additional components from NavigationExperimental that you may find useful. Since I am feeling like building navigation stacks today, I'll go ahead and pick out NavigationCardStack and NavigationStateUtils.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { NavigationExperimental } from 'react-native';
|
||||||
|
|
||||||
|
const {
|
||||||
|
CardStack: NavigationCardStack,
|
||||||
|
StateUtils: NavigationStateUtils,
|
||||||
|
} = NavigationExperimental;
|
||||||
|
```
|
||||||
|
|
||||||
|
As I said earlier, NavigationExperimental takes a different approach than Navigator and NavigatorIOS. Using it to build a navigation stack requires a few more steps than the stateful components, but the payoff is worth it.
|
||||||
|
|
||||||
|
### Step 1. Define Initial State and Top Level Component
|
||||||
|
|
||||||
|
Create a new component for your application. This will be the top-level object, so we will define the initial state here. The navigation state will be defined in the `navigationState` key, where we define our initial route:
|
||||||
|
|
||||||
|
```js
|
||||||
|
class BleedingEdgeApplication extends Component {
|
||||||
|
constructor(props, context) {
|
||||||
|
super(props, context);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
// This defines the initial navigation state.
|
||||||
|
navigationState: {
|
||||||
|
index: 0, // Starts with first route focused.
|
||||||
|
routes: [{key: 'My Initial Scene'}], // Starts with only one route.
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// We'll define this function later - hang on
|
||||||
|
this._onNavigationChange = this._onNavigationChange.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onNavigationChange(type) {
|
||||||
|
// It's literally the next step. We'll get to it!
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Text>This is a placeholder. We will come back to this and render our navigation here later.</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Alright, now we have a simple stateful component that doesn't do much at all. We can change that. Our initial state contains one route, and the current index. That looks suspiciously just like our initial route definition in Navigator. Do you remember which actions its navigator object provided?
|
||||||
|
|
||||||
|
Push and pop, of course. That seems pretty straightforward to implement. I promised you earlier we would be using reducers to manage state at the top-level object. Sit tight.
|
||||||
|
|
||||||
|
### Step 2. Reducing the Navigation State
|
||||||
|
|
||||||
|
NavigationExperimental comes built-in with a some useful reducers, and they are all available as part of NavigationStateUtils. The two we will be using right now are called -- yep -- push and pop. They take a navigationState object, and return a new navigationState object.
|
||||||
|
|
||||||
|
We can use them to write our `_onNavigationChange` function which, given a "push" or "pop" action, will reduce the state accordingly.
|
||||||
|
|
||||||
|
```js
|
||||||
|
_onNavigationChange(type) {
|
||||||
|
// Extract the navigationState from the current state:
|
||||||
|
let {navigationState} = this.state;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'push':
|
||||||
|
// Push a new route, which in our case is an object with a key value.
|
||||||
|
// I am fond of cryptic keys (but seriously, keys should be unique)
|
||||||
|
const route = {key: 'Route-' + Date.now()};
|
||||||
|
|
||||||
|
// Use the push reducer provided by NavigationStateUtils
|
||||||
|
navigationState = NavigationStateUtils.push(navigationState, route);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'pop':
|
||||||
|
// Pop the current route using the pop reducer.
|
||||||
|
navigationState = NavigationStateUtils.pop(navigationState);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NavigationStateUtils gives you back the same `navigationState` if nothing
|
||||||
|
// has changed. We will only update state if it has changed.
|
||||||
|
if (this.state.navigationState !== navigationState) {
|
||||||
|
// Always use setState() when setting a new state!
|
||||||
|
this.setState({navigationState});
|
||||||
|
// If you are new to ES6, the above is equivalent to:
|
||||||
|
// this.setState({navigationState: navigationState});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Cool. I'm getting the hang of this. This is the heart of NavigationExperimental. We are only handling two actions here, but a more complex application could also take into account a "back" action (e.g. Android back button), as well as handle the transition between several tabs in a tabbed application.
|
||||||
|
|
||||||
|
I am still missing the initial scene that will be rendered (as well as the actual navigator that will wrap it, but lets not get ahead of ourselves).
|
||||||
|
|
||||||
|
### Step 3. Define Scenes
|
||||||
|
|
||||||
|
First I want to define a Row component out of convenience. It displays some text and can call some function when pressed.
|
||||||
|
|
||||||
|
```js
|
||||||
|
class TappableRow extends Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<TouchableHighlight
|
||||||
|
style={styles.row}
|
||||||
|
underlayColor="#D0D0D0"
|
||||||
|
onPress={this.props.onPress}>
|
||||||
|
<Text style={styles.buttonText}>
|
||||||
|
{this.props.text}
|
||||||
|
</Text>
|
||||||
|
</TouchableHighlight>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now I will define my actual scene. It uses a scroll view to display a vertical list of items. The first row displays the current route's key, and two more rows will call our theoretical navigator's push and pop functions.
|
||||||
|
|
||||||
|
```js
|
||||||
|
class MyVeryComplexScene extends Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<ScrollView style={styles.scrollView}>
|
||||||
|
<Text style={styles.row}>
|
||||||
|
Route: {this.props.route.key}
|
||||||
|
</Text>
|
||||||
|
<TappableRow
|
||||||
|
text="Tap me to load the next scene"
|
||||||
|
onPress={this.props.onPushRoute}
|
||||||
|
/>
|
||||||
|
<TappableRow
|
||||||
|
text="Tap me to go back"
|
||||||
|
onPress={this.props.onPopRoute}
|
||||||
|
/>
|
||||||
|
</ScrollView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 4. Create a Navigation Stack
|
||||||
|
|
||||||
|
Now that I have defined the state and a function to manage it, I think I can go ahead and create a proper navigator component now. While I'm at it, I'll render my scene after configuring it with the current route's props.
|
||||||
|
|
||||||
|
```js
|
||||||
|
class MyVerySimpleNavigator extends Component {
|
||||||
|
|
||||||
|
// This sets up the methods (e.g. Pop, Push) for navigation.
|
||||||
|
constructor(props, context) {
|
||||||
|
super(props, context);
|
||||||
|
|
||||||
|
this._onPushRoute = this.props.onNavigationChange.bind(null, 'push');
|
||||||
|
this._onPopRoute = this.props.onNavigationChange.bind(null, 'pop');
|
||||||
|
|
||||||
|
this._renderScene = this._renderScene.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we finally get to use the `NavigationCardStack` to render the scenes.
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<NavigationCardStack
|
||||||
|
onNavigateBack={this._onPopRoute}
|
||||||
|
navigationState={this.props.navigationState}
|
||||||
|
renderScene={this._renderScene}
|
||||||
|
style={styles.navigator}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render a scene for route.
|
||||||
|
// The detailed spec of `sceneProps` is defined at `NavigationTypeDefinition`
|
||||||
|
// as type `NavigationSceneRendererProps`.
|
||||||
|
// Here you could choose to render a different component for each route, but
|
||||||
|
// we'll keep it simple.
|
||||||
|
_renderScene(sceneProps) {
|
||||||
|
return (
|
||||||
|
<MyVeryComplexScene
|
||||||
|
route={sceneProps.scene.route}
|
||||||
|
onPushRoute={this._onPushRoute}
|
||||||
|
onPopRoute={this._onPopRoute}
|
||||||
|
onExit={this.props.onExit}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
That's it -- so close to the finish line I can smell it. Lets plug our new navigator into our top-level component:
|
||||||
|
|
||||||
|
```js
|
||||||
|
class BleedingEdgeApplication extends Component {
|
||||||
|
|
||||||
|
// constructor and other methods omitted for clarity
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<MyVerySimpleNavigator
|
||||||
|
navigationState={this.state.navigationState}
|
||||||
|
onNavigationChange={this._onNavigationChange}
|
||||||
|
onExit={this._exit}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We're done! Bask in the glory of NavigationExperimental.
|
||||||
|
|
||||||
|
#### Hey -- I think you are missing something.
|
||||||
|
|
||||||
|
(Oh yes, sorry about that -- here's our missing imports and styles.)
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { NavigationExperimental, PixelRatio, ScrollView, StyleSheet, Text, TouchableHighlight } from 'react-native';
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
navigator: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
scrollView: {
|
||||||
|
marginTop: 64
|
||||||
|
},
|
||||||
|
row: {
|
||||||
|
padding: 15,
|
||||||
|
backgroundColor: 'white',
|
||||||
|
borderBottomWidth: 1 / PixelRatio.get(),
|
||||||
|
borderBottomColor: '#CDCDCD',
|
||||||
|
},
|
||||||
|
rowText: {
|
||||||
|
fontSize: 17,
|
||||||
|
},
|
||||||
|
buttonText: {
|
||||||
|
fontSize: 17,
|
||||||
|
fontWeight: '500',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Homework
|
||||||
|
|
||||||
|
You are now an expert navigator. Take a look at [NavigationExperimental in UIExplorer](https://github.com/facebook/react-native/tree/master/Examples/UIExplorer/NavigationExperimental) to learn how to implement other types of navigation hierarchies, such as a tabbed application with multiple navigation stacks.
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 151 KiB |
Binary file not shown.
After Width: | Height: | Size: 63 KiB |
Loading…
Reference in New Issue