├── .gitignore
├── Docs
├── images
│ └── anim_stack.gif
├── Guide.md
├── NavigationOverview.md
└── Navigation.md
└── ReadMe.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/Docs/images/anim_stack.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ericvicenti/navigation-rfc/HEAD/Docs/images/anim_stack.gif
--------------------------------------------------------------------------------
/ReadMe.md:
--------------------------------------------------------------------------------
1 | # Note: Use React Navigation now instead
2 |
3 | This project was developed in early 2016 and has since matured and combined with Expo's Ex-Navigation project to form a new effort: [React Navigation](https://reactnavigation.org). Please use that instead, and collaborate with the community to improve it!
4 |
5 | ## DEPRECATED NavigationExperimental
6 |
7 | This was an 2016 RFC for a new Navigation system for react native, with focus on the following improvements over ``:
8 |
9 | - Single-directional data flow, using reducers to manipulate top-level state object
10 | - Navigation logic and routing must be independent from view logic to allow for a variety of native and js-based navigation views
11 | - Improved modularity of scene animations, gestures, and navigation bars
12 |
13 | If you're confused about all the navigation options, look at the [Navigation Comparison](Docs/NavigationOverview.md) doc.
14 |
15 | ## Code
16 |
17 | NavigationExperimental has recently been moved from here to the open source RN repo: https://github.com/facebook/react-native/commit/a3085464f6ea36fc6b53cd0c711c048ffb1516f9
18 |
19 | - Core Utilities and Components: https://github.com/facebook/react-native/tree/master/Libraries/NavigationExperimental
20 | - Animated Card and Header: https://github.com/facebook/react-native/tree/master/Libraries/CustomComponents/NavigationExperimental
21 | - Examples: https://github.com/facebook/react-native/tree/master/Examples/UIExplorer/js/NavigationExperimental
22 |
23 |
24 | ## Docs
25 |
26 | - [Navigator Comparison](Docs/NavigationOverview.md)
27 | - [Complete Navigation API](Docs/Navigation.md)
28 | - [Navigation Walkthrough](Docs/Guide.md)
29 |
--------------------------------------------------------------------------------
/Docs/Guide.md:
--------------------------------------------------------------------------------
1 | # Navigation Guide
2 |
3 | Lets build the navigation for a simple chat app. First, we should decide on the structure of our application state. Because we can have a stack of screens in our app, we will store the list of scenes in an array:
4 |
5 | ```javascript
6 | class MyApp extends React.Component {
7 | constructor(props) {
8 | super(props);
9 | this.state = {
10 | scenes: [
11 | {key: 'home'}, // this represents the application home screen
12 | ],
13 | };
14 | }
15 | ```
16 |
17 | For our render function, we want to display the topmost/current scene in the stack.
18 |
19 | ```javascript
20 | render() {
21 | const scene = this.state.scenes[this.state.scenes.length - 1];
22 | if (scene.key === 'home') {
23 | return ;
24 | }
25 | if (scene.type === 'chat') {
26 | return ;
27 | }
28 | return null;
29 | }
30 | ```
31 |
32 | To open a chat screen, we could add a method called `openChat`:
33 |
34 | ```javascript
35 | openChat(id) {
36 | this.setState({
37 | scenes: [
38 | ...this.state.scenes,
39 | { type: 'chat', key: id }
40 | ],
41 | });
42 | }
43 | ```
44 |
45 | And if we wanted to go back, we could implement a back method like this:
46 |
47 | ```javascript
48 | goBack() {
49 | if (this.state.scenes.length > 1) {
50 | this.setState({
51 | scenes: this.state.scenes.slice(0, this.state.scenes.length - 1),
52 | });
53 | }
54 | }
55 | ```
56 |
57 | However, it quickly becomes complicated to maintain independent methods for every navigation action in your app. To fix this, we can delegate all of the navigation logic to a reducer:
58 |
59 | ```javascript
60 | constructor(props) {
61 | super(props);
62 | this.state = AppReducer(null, { type: 'init' });
63 | }
64 | dispatch(action) {
65 | this.setState(AppReducer(this.state, action));
66 | }
67 | ```
68 |
69 | Our reducer would then look like this:
70 |
71 | ```javascript
72 | function AppReducer(lastState, action) {
73 | let state = lastState;
74 | if (!state) {
75 | state = {
76 | scenes: [
77 | {key: 'home'}
78 | ],
79 | };
80 | }
81 | if (action.type === 'back' && state.scenes.length > 1) {
82 | return {
83 | scenes: state.scenes.slice(0, this.state.scenes.length - 1),
84 | };
85 | }
86 | if (action.type === 'openChat') {
87 | return {
88 | scenes: [
89 | ...state.scenes,
90 | {
91 | type: 'chat',
92 | key: action.id
93 | }
94 | ],
95 | };
96 | }
97 | return state;
98 | }
99 | ```
100 |
101 | Now, we can easily implement our navigation methods as the following:
102 |
103 | ```javascript
104 | openChat(id) {
105 | this.dispatch({ type: 'openChat', id });
106 | }
107 | goBack() {
108 | this.dispatch({ type: 'back' });
109 | }
110 | ```
111 |
112 | And now that we have implemented `this.dispatch`, we can give access to allow sub-components to dispatch actions:
113 |
114 | ```javascript
115 | render() {
116 | const scene = this.state.scenes[this.state.scenes.length - 1];
117 | if (scene.key === 'home') {
118 | return (
119 |
122 | );
123 | }
124 | if (scene.type === 'chat') {
125 | return (
126 |
130 | );
131 | }
132 | return null;
133 | }
134 | ```
135 |
136 | ```javascript
137 | function HomeView(props) {
138 | return
139 | {
141 | props.dispatch({ type: 'openChat', id: 'A' });
142 | }}>
143 | This is the home screen. Tap to open Chat A.
144 | ;
145 | }
146 | function ChatView(props) {
147 | return
148 | {
150 | props.dispatch({ type: 'back' });
151 | }}>
152 | This is chat {props.id}. Tap to go back home.
153 | ;
154 | }
155 | ```
156 |
157 | Now we have a simple application which supports opening the chat view and returning to the home screen. See the code here: https://gist.github.com/ericvicenti/d8f00f4edaf50a8773d734667e32b1a7
158 |
159 |
160 | ### Android Back Button
161 |
162 | The back button is critical for navigation on Android, so we can go ahead and add support for it now. Our application can subscribe to back button events and pass them into the dispatch:
163 |
164 | ### Link support
165 |
--------------------------------------------------------------------------------
/Docs/NavigationOverview.md:
--------------------------------------------------------------------------------
1 | # Navigator Comparison
2 |
3 | The differences between [Navigator](docs/navigator.html)
4 | and [NavigatorIOS](docs/navigatorios.html) have been a common
5 | source of confusion for newcomers. Facebook is transitioning from `Navigator` to `NavigationExperimental`, which will be the supported navigation library going forward.
6 |
7 | NavigationExperimental is often referred to as "the new navigator", but it is actually a new approach to navigation logic that allows ANY view to act as a navigation view. It includes a pre-built component for managing scene animations called `NavigationTransitioner`. The views within can each define their own gestures and animations. There are pre-built scene and overlay components that are meant to look consistent with platform conventions.
8 |
9 | `Navigator` and `NavigatorIOS` are both stateful components that allow you to
10 | manage the navigation in your app between various "scenes" (another word
11 | for screens). They manage a route stack and allow you to pop, push, and
12 | replace states. In this way, [they are similar to the html5 history
13 | API](https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history).
14 | The primary distinction between the two is that `NavigatorIOS` leverages
15 | the iOS
16 | [UINavigationController](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UINavigationController_Class/)
17 | class, and `Navigator` re-implements that functionality entirely in
18 | JavaScript as a React component. A corollary of this is that `Navigator`
19 | is to be compatible with Android and iOS, whereas `NavigatorIOS` will
20 | only work on the one platform. If multiple of these stateful navigation components are used in an app, it can become tricky to coordinate navigation transitions between them.
21 |
22 |
23 | ## NavigationExperimental
24 |
25 | - NavigationRootContainer allows navigation state to be stored at the top-level of the app
26 | - Uses a reducer to declaratively set up transitions in navigation state
27 | - Can save state to disk to persist navigation state over refreshes or app updates
28 | - Listens to Linking for url opening, and BackAndroid for easy back button support
29 | - NavigationReducer contains pre-built reducers to manage transitions in navigation state
30 | - Reducers can be combined with each other to set up advanced navigation logic
31 | - The navigation logic can work with any view
32 | - `NavigationTransitioner` is the component that can be used to manage animations between a set of scenes, and can be used to replace a `Navigator` or `NavigatorIOS` component.
33 | - Each scene can be totally custom and manage its own animations and gestures
34 | - Can have an overlay/header that can be syncronized with the animations of the scenes
35 | - `NavigationCard` and `NavigationHeader` are available as pre-built scenes and overlays which can be used with `NavigationTransitioner`
36 |
37 |
38 | ## Navigator
39 |
40 | - Facebook is dropping support for Navigator and will focus on NavigationExperimental from now on
41 | - Owns navigations state and has an imperative API, which goes against the React philosophy of single-directional data flow
42 | - Scene animations and gestures are difficult to customize
43 | - Gestures are handled by the Navigator and cannot be customised on a per-scene basis
44 | - Animation customization is obscure because it predates the Animated library
45 | - Works on iOS and Android.
46 | - Includes a simple navigation bar component similar to the default `NavigatorIOS` bar: `Navigator.NavigationBar`, and another with breadcrumbs called `Navigator.BreadcrumbNavigationBar`. See the UIExplorer demo to try them out and see how to use them.
47 | - Animations are less refined than Apple's, which you get from `NavigatorIOS`.
48 | - You can provide your own navigation bar by passing it through the `navigationBar` prop.
49 |
50 |
51 | ## NavigatorIOS
52 |
53 | - Has an imperative API that does not play nice with the rest of the app.
54 | - Small, limited API makes it much less customizable than `Navigator` or `NavigationStackView`
55 | - Development belongs to open-source community - not used by the React Native team on their apps.
56 | - A result of this is that there is currently a backlog of unresolved bugs, nobody who uses this has stepped up to take ownership for it yet.
57 | - If the community refactors it to be declarative, it will work nicely with `NavigationExperimental`
58 | - Wraps UIKit, so it works exactly the same as it would on another native app. Lives in Objective-C and JavaScript.
59 | - Consequently, you get the animations and behavior that Apple has developed.
60 | - iOS only.
61 | - Includes a navigation bar by default; this navigation bar is not a React Native view component and the style can only be slightly modified.
62 |
63 | For most non-trivial apps, you will want to use `NavigationExperimental` - it won't be long before you run into issues when trying to do anything complex with `NavigatorIOS`.
64 |
--------------------------------------------------------------------------------
/Docs/Navigation.md:
--------------------------------------------------------------------------------
1 | # NavigationExperimental
2 |
3 | ## Navigation State
4 |
5 | The entire navigation state of your app can be modeled with NavigationStates. A `NavigationState` is an object with a `key` and some optional arbitrary data:
6 |
7 | ```js
8 | const myState = {
9 | key: 'myPage0',
10 | myType: 'ExamplePage',
11 | myParams: {foo: 'bar'},
12 | }
13 | ```
14 |
15 | A `NavigationParentState` contains a set of routes and has an index which refers to a particular route.
16 |
17 | ```js
18 | const myState = {
19 | key: 'myAppTabs',
20 | routes: [
21 | {key: 'home'},
22 | {key: 'notifs'},
23 | {key: 'settings'},
24 | ],
25 | index: 1, // points to the 'notifs' tab
26 | }
27 | ```
28 |
29 | The navigation state types are available in `NavigationStateUtils`, along with a variety of utility functions which can be used to make changes to `NavigationParentState`s
30 |
31 | ## Containers
32 |
33 | We provide a default top-level component to maintain the state of your navigation and handle persistence.
34 |
35 | If you are using redux or flux, you will probably not need `NavigationContainer`. You can use your existing stores and providers.
36 |
37 | ### NavigationRootContainer
38 |
39 | The developer can set the reducer for the root container, which will contain all of the navigation logic for the app. Our navigation reducers will take in the last navigation state, an action that we need to handle, and it will output a new navigation state for our app. To get the initial state, the reducer will be called without a previous state or an action.
40 |
41 | ```js
42 | (
45 | Currently at {navigationState.routes[navigationState.index]}
46 | ```
47 |
48 | It also provides a handler for navigation actions, and allows the reducer to be customised:
49 |
50 |
51 | ### NavigationContainer.create
52 |
53 | It can be very tedious to pass the `onNavigate` prop around throughout your entire application. To alleviate this, we have provided a higher-order "container" component that you can use to provide components with this prop, so long as they are rendered under a `NavigationRootContainer`:
54 |
55 | ```js
56 | }
59 | ...
60 |
61 | class ExampleComponent {
62 | render() {
63 | { this.props.onNavigate(new ExampleAction()) }}>
64 | This action will work, even though `onNavigate` was not directly passed in
65 |
66 | }
67 | }
68 | ExampleComponent = NavigationContainer.create(ExampleComponent);
69 | ```
70 |
71 | If `onNavigate` is actually passed to the container as a prop, it will override the handler for the contained component and for all sub-containers.
72 |
73 | ## Reducers
74 |
75 | A navigation reducer is an action handler that returns the current navigation state. When calling navigation reducers, you provide an optional previous state, and a navigation action with a `type` string.
76 |
77 | ```js
78 | let state = MyReducer(null, { type: 'InitialAction' });
79 | > {
80 | key: 'Root',
81 | index: 0,
82 | routes: [
83 | {key: 'Home'},
84 | ]
85 | }
86 |
87 | state = MyReducer(state, { type: 'PushPerson', name: 'Christopher' });
88 | > {
89 | key: 'Root',
90 | index: 1,
91 | routes: [
92 | {key: 'Home'},
93 | {key: 'Person0', name: 'Christopher'},
94 | ]
95 | }
96 | ```
97 |
98 | ### Stack Reducer
99 |
100 | A common type of navigation logic is a 'stack', which can be handled by the stack reducer:
101 |
102 | ```js
103 | const MyReducer = NavigationStackReducer({
104 | // First, define the initial parent state that will be used if there was no previous state.
105 | initialState: {
106 | key: 'Root',
107 | index: 0,
108 | routes: [
109 | {key: 'Home'},
110 | ]
111 | },
112 | getPushedReducerForAction: (action) => {
113 | if (action.type === 'PushPerson') {
114 | // We need to push some additional state, that will be defined by this reducer:
115 | return () => ({
116 | key: 'Person'+(i++),
117 | name: action.name,
118 | });
119 | }
120 | // In this case we do not need to push, so our reducer for this action is nothing
121 | return null;
122 | },
123 | });
124 |
125 | let state = MyReducer(null, { type: 'InitAction' });
126 | > {
127 | key: 'Root',
128 | index: 0,
129 | routes: [
130 | {key: 'Home'},
131 | ]
132 | }
133 |
134 | state = MyReducer(state, { type: 'PushPerson', name: 'Christopher' });
135 | > {
136 | key: 'Root',
137 | index: 1,
138 | routes: [
139 | {key: 'Home'},
140 | {key: 'Person0', name: 'Christopher'},
141 | ]
142 | }
143 |
144 | // The back action can be used to pop:
145 | state = MyReducer(state, NavigationRootContainer.getBackAction());
146 | > {
147 | key: 'Root',
148 | index: 0,
149 | routes: [
150 | {key: 'Home'},
151 | ]
152 | }
153 | ```
154 |
155 | Usage of the stack reducer can also include sub-reducers, which will require you to implement `getReducerForState`. This will return a sub-reducer for a given sub-state. The sub-reducer for the active sub-state will be used.
156 |
157 | ### Tabs Reducer
158 |
159 | Tabs reducer allows you to have several sub-reducers, with one 'active' one. For each action that is sent to the tabs reducer, it will first attempt to use the active sub-reducer. If the reducer does not return a new sub-state, then the other reducers will get a chance to handle it. If a different tab reducer does handle it, the tabs reducer will apply the new sub-state and switch the active tab.
160 |
161 | ### Find Reducer
162 |
163 | A common pattern with reducers is to combine several reducers, and stop when one reducer has returned a new state. The find reducer takes an array of reducers and returns a reducer that will iterate through each one of them until the state has changed. If none of them provide a new state, the find reducer will return the default state.
164 |
165 | ## Views
166 |
167 | ### NavigationView
168 |
169 | A simple view that will render a scene for the currently presented sub-state. The most common use-case is for tabs, where no transition is needed.
170 |
171 | ### NavigationAnimatedView
172 |
173 | [NavigationAnimatedView](AnimatedView.md) is the spiritual successor to Navigator. In addition to adopting a declaritive API, it uses the Animated library to delegate animations and gestures to the scenes.
174 |
175 | NavigationCard and NavigationHeader are the included implementations of scenes and overlays for NavigationAnimatedView, which are intended to look similar to platform conventions.
176 |
177 | ### NavigationCard
178 |
179 | ```js
180 | (
183 |
189 |
190 |
191 | )}
192 | />
193 | ```
194 |
195 |
196 | ### NavigationHeader
197 |
198 | ```js
199 | (
202 | state.key}
206 | />
207 | )}
208 | renderScene={this._renderScene}
209 | />
210 | ```
211 |
212 | ### NavigationCardStack
213 |
214 | A component that wraps `NavigationAnimatedView` and automatically renders a `NavigationCard` for each scene. Similar to the legacy `Navigator` because the animations and gestures are built-in.
215 |
216 | Usage:
217 |
218 | ```js
219 | render() {
220 | return (
221 |
224 |
228 | }
229 | renderOverlay={props => }
230 | navigationState={{
231 | key: 'MyPetStack',
232 | index: 2,
233 | routes: [
234 | {key: 'Pluto', species: 'dog'},
235 | {key: 'Snoopy', species: 'dog'},
236 | {key: 'Garfield', species: 'cat'},
237 | ]
238 | }}
239 | />
240 | );
241 | }
242 | ```
243 |
244 | [See the working example code in UIExplorer](https://github.com/facebook/react-native/blob/master/Examples/UIExplorer/NavigationExperimental/NavigationCardStackExample.js)
245 |
--------------------------------------------------------------------------------