Skip to content

Commit dcb68db

Browse files
Eric Vicentifacebook-github-bot-6
authored andcommitted
Add sub-reducer support to NavigationStackReducer
Summary: Revise APIs of reducers, and ensure the stack reducer can support sub-reducers Reviewed By: javache Differential Revision: D2959915 fb-gh-sync-id: 20b28b9ead7ace3373489a806486999048d32aef shipit-source-id: 20b28b9ead7ace3373489a806486999048d32aef
1 parent 876ecb2 commit dcb68db

10 files changed

Lines changed: 249 additions & 365 deletions

File tree

Examples/UIExplorer/NavigationExperimental/NavigationAnimatedExample.js

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,20 @@ var {
2929
} = NavigationExperimental;
3030

3131
const NavigationBasicReducer = NavigationReducer.StackReducer({
32-
initialStates: [
33-
{key: 'First Route'}
34-
],
35-
matchAction: action => action.type === 'push',
36-
actionStateMap: action => ({key: action.key}),
32+
getPushedReducerForAction: (action) => {
33+
if (action.type === 'push') {
34+
return (state) => state || {key: action.key};
35+
}
36+
return null;
37+
},
38+
getReducerForState: (initialState) => (state) => state || initialState,
39+
initialState: {
40+
key: 'AnimatedExampleStackKey',
41+
index: 0,
42+
children: [
43+
{key: 'First Route'},
44+
],
45+
},
3746
});
3847

3948
class NavigationAnimatedExample extends React.Component {

Examples/UIExplorer/NavigationExperimental/NavigationBasicExample.js

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,21 @@ const {
2626
} = NavigationExperimental;
2727
const StackReducer = NavigationReducer.StackReducer;
2828

29-
const NavigationBasicReducer = StackReducer({
30-
initialStates: [
31-
{key: 'first_page'}
32-
],
33-
matchAction: action => true,
34-
actionStateMap: action => ({key: action}),
29+
const NavigationBasicReducer = NavigationReducer.StackReducer({
30+
getPushedReducerForAction: (action) => {
31+
if (action.type === 'push') {
32+
return (state) => state || {key: action.key};
33+
}
34+
return null;
35+
},
36+
getReducerForState: (initialState) => (state) => state || initialState,
37+
initialState: {
38+
key: 'BasicExampleStackKey',
39+
index: 0,
40+
children: [
41+
{key: 'First Route'},
42+
],
43+
},
3544
});
3645

3746
const NavigationBasicExample = React.createClass({
@@ -51,13 +60,13 @@ const NavigationBasicExample = React.createClass({
5160
<NavigationExampleRow
5261
text={`Push page #${navState.children.length}`}
5362
onPress={() => {
54-
onNavigate('page #' + navState.children.length);
63+
onNavigate({ type: 'push', key: 'page #' + navState.children.length });
5564
}}
5665
/>
5766
<NavigationExampleRow
5867
text="pop"
5968
onPress={() => {
60-
onNavigate(StackReducer.PopAction());
69+
onNavigate(NavigationRootContainer.getBackAction());
6170
}}
6271
/>
6372
<NavigationExampleRow

Examples/UIExplorer/NavigationExperimental/NavigationCompositionExample.js

Lines changed: 70 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
1111
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
1212
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
13-
*/
13+
*
14+
* @flow
15+
*/
1416
'use strict';
1517

1618
const React = require('react-native');
@@ -32,32 +34,34 @@ const {
3234
const NavigationExampleRow = require('./NavigationExampleRow');
3335
const NavigationExampleTabBar = require('./NavigationExampleTabBar');
3436

37+
import type {NavigationParentState} from 'NavigationStateUtils';
38+
3539
const ExampleExitAction = () => ({
3640
isExitAction: true,
3741
});
3842
ExampleExitAction.match = (action) => (
3943
action && action.isExitAction === true
4044
);
4145

42-
const ExamplePageAction = (type) => ({
46+
const PageAction = (type) => ({
4347
type,
4448
isPageAction: true,
4549
});
46-
ExamplePageAction.match = (action) => (
50+
PageAction.match = (action) => (
4751
action && action.isPageAction === true
4852
);
4953

50-
const ExampleSettingsPageAction = (type) => ({
51-
...ExamplePageAction(type),
52-
isSettingsPageAction: true,
54+
const ExampleProfilePageAction = (type) => ({
55+
...PageAction(type),
56+
isProfilePageAction: true,
5357
});
54-
ExampleSettingsPageAction.match = (action) => (
55-
action && action.isSettingsPageAction === true
58+
ExampleProfilePageAction.match = (action) => (
59+
action && action.isProfilePageAction === true
5660
);
5761

58-
const ExampleInfoAction = () => ExamplePageAction('InfoPage');
62+
const ExampleInfoAction = () => PageAction('InfoPage');
5963

60-
const ExampleNotifSettingsAction = () => ExampleSettingsPageAction('NotifSettingsPage');
64+
const ExampleNotifProfileAction = () => ExampleProfilePageAction('NotifProfilePage');
6165

6266
const _jsInstanceUniqueId = '' + Date.now();
6367
let _uniqueIdCount = 0;
@@ -68,70 +72,70 @@ function pageStateActionMap(action) {
6872
};
6973
}
7074

71-
function getTabActionMatcher(key) {
72-
return function (action) {
73-
if (!ExamplePageAction.match(action)) {
74-
return false;
75-
}
76-
if (ExampleSettingsPageAction.match(action)) {
77-
return key === 'settings';
78-
}
79-
return true;
80-
};
81-
}
82-
83-
var ExampleTabs = [
84-
{
85-
label: 'Account',
86-
reducer: NavigationReducer.StackReducer({
87-
initialStates: [
88-
{type: 'AccountPage', key: 'base'}
89-
],
90-
key: 'account',
91-
matchAction: getTabActionMatcher('account'),
92-
actionStateMap: pageStateActionMap,
75+
const ExampleAppReducer = NavigationReducer.TabsReducer({
76+
key: 'AppNavigationState',
77+
initialIndex: 0,
78+
tabReducers: [
79+
NavigationReducer.StackReducer({
80+
getPushedReducerForAction: (action) => {
81+
if (PageAction.match(action) && !ExampleProfilePageAction.match(action)) {
82+
return (state) => (state || pageStateActionMap(action));
83+
}
84+
return null;
85+
},
86+
initialState: {
87+
key: 'notifs',
88+
index: 0,
89+
children: [
90+
{key: 'base', type: 'NotifsPage'},
91+
],
92+
},
9393
}),
94-
},
95-
{
96-
label: 'Notifications',
97-
reducer: NavigationReducer.StackReducer({
98-
initialStates: [
99-
{type: 'NotifsPage', key: 'base'}
100-
],
101-
key: 'notifs',
102-
matchAction: getTabActionMatcher('notifs'),
103-
actionStateMap: pageStateActionMap,
94+
NavigationReducer.StackReducer({
95+
getPushedReducerForAction: (action) => {
96+
if (PageAction.match(action) && !ExampleProfilePageAction.match(action)) {
97+
return (state) => (state || pageStateActionMap(action));
98+
}
99+
return null;
100+
},
101+
initialState: {
102+
key: 'settings',
103+
index: 0,
104+
children: [
105+
{key: 'base', type: 'SettingsPage'},
106+
],
107+
},
104108
}),
105-
},
106-
{
107-
label: 'Settings',
108-
reducer: NavigationReducer.StackReducer({
109-
initialStates: [
110-
{type: 'SettingsPage', key: 'base'}
111-
],
112-
key: 'settings',
113-
matchAction: getTabActionMatcher('settings'),
114-
actionStateMap: pageStateActionMap,
109+
NavigationReducer.StackReducer({
110+
getPushedReducerForAction: (action) => {
111+
if (PageAction.match(action) || ExampleProfilePageAction.match(action)) {
112+
return (state) => (state || pageStateActionMap(action));
113+
}
114+
return null;
115+
},
116+
initialState: {
117+
key: 'profile',
118+
index: 0,
119+
children: [
120+
{key: 'base', type: 'ProfilePage'},
121+
],
122+
},
115123
}),
116-
},
117-
];
118-
119-
const ExampleAppReducer = NavigationReducer.TabsReducer({
120-
tabReducers: ExampleTabs.map(tab => tab.reducer),
124+
],
121125
});
122126

123127
function stateTypeTitleMap(pageState) {
124128
switch (pageState.type) {
125-
case 'AccountPage':
126-
return 'Account Page';
129+
case 'ProfilePage':
130+
return 'Profile Page';
127131
case 'NotifsPage':
128132
return 'Notifications';
129133
case 'SettingsPage':
130134
return 'Settings';
131135
case 'InfoPage':
132136
return 'Info Page';
133-
case 'NotifSettingsPage':
134-
return 'Notification Settings';
137+
case 'NotifProfilePage':
138+
return 'Page in Profile';
135139
}
136140
}
137141

@@ -173,9 +177,9 @@ class ExampleTabScreen extends React.Component {
173177
}}
174178
/>
175179
<NavigationExampleRow
176-
text="Open notifs settings in settings tab"
180+
text="Open a page in the profile tab"
177181
onPress={() => {
178-
this.props.onNavigate(ExampleNotifSettingsAction());
182+
this.props.onNavigate(ExampleNotifProfileAction());
179183
}}
180184
/>
181185
<NavigationExampleRow
@@ -196,19 +200,19 @@ class NavigationCompositionExample extends React.Component {
196200
return (
197201
<NavigationRootContainer
198202
reducer={ExampleAppReducer}
199-
persistenceKey="NavigationCompositionExampleState"
203+
persistenceKey="NavigationCompositionState"
200204
ref={navRootContainer => { this.navRootContainer = navRootContainer; }}
201205
renderNavigation={this.renderApp.bind(this)}
202206
/>
203207
);
204208
}
205-
handleBackAction() {
209+
handleBackAction(): boolean {
206210
return (
207211
this.navRootContainer &&
208212
this.navRootContainer.handleNavigation(NavigationRootContainer.getBackAction())
209213
);
210214
}
211-
renderApp(navigationState, onNavigate) {
215+
renderApp(navigationState: NavigationParentState, onNavigate: Function) {
212216
if (!navigationState) {
213217
return null;
214218
}

Examples/UIExplorer/UIExplorerNavigationReducer.js

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,20 @@ export type UIExplorerNavigationState = {
3636
};
3737

3838
const UIExplorerStackReducer = StackReducer({
39-
key: 'UIExplorerMainStack',
40-
initialStates: [
41-
{key: 'AppList'},
42-
],
43-
initialIndex: 0,
44-
matchAction: action => action.openExample && !!UIExplorerList.Modules[action.openExample],
45-
actionStateMap: action => ({ key: action.openExample, }),
39+
getPushedReducerForAction: (action) => {
40+
if (action.type === 'UIExplorerExampleAction' && UIExplorerList.Modules[action.openExample]) {
41+
return (state) => state || {key: action.openExample};
42+
}
43+
return null;
44+
},
45+
getReducerForState: (initialState) => (state) => state || initialState,
46+
initialState: {
47+
key: 'UIExplorerMainStack',
48+
index: 0,
49+
children: [
50+
{key: 'AppList'},
51+
],
52+
},
4653
});
4754

4855
function UIExplorerNavigationReducer(lastState: ?UIExplorerNavigationState, action: any): UIExplorerNavigationState {
@@ -86,7 +93,7 @@ function UIExplorerNavigationReducer(lastState: ?UIExplorerNavigationState, acti
8693
if (newStack !== lastState.stack) {
8794
return {
8895
externalExample: null,
89-
stack: UIExplorerStackReducer(null, action),
96+
stack: newStack,
9097
}
9198
}
9299
return lastState;

Libraries/CustomComponents/NavigationExperimental/NavigationCard.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
'use strict';
2929

3030
const Animated = require('Animated');
31-
const NavigationReducer = require('NavigationReducer');
31+
const NavigationRootContainer = require('NavigationRootContainer');
3232
const NavigationContainer = require('NavigationContainer');
3333
const PanResponder = require('PanResponder');
3434
const Platform = require('Platform');
@@ -95,7 +95,7 @@ class NavigationCard extends React.Component {
9595
const doesPop = (xRatio + vx) > 0.45;
9696
if (doesPop) {
9797
// todo: add an action which accepts velocity of the pop action/gesture, which is caught and used by NavigationAnimatedView
98-
this.props.onNavigate(NavigationReducer.StackReducer.PopAction());
98+
this.props.onNavigate(NavigationRootContainer.getBackAction());
9999
return;
100100
}
101101
Animated.spring(this.props.position, {

Libraries/CustomComponents/NavigationExperimental/NavigationHeader.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
const Animated = require('Animated');
3131
const Image = require('Image');
3232
const NavigationContainer = require('NavigationContainer');
33-
const NavigationReducer = require('NavigationReducer');
33+
const NavigationRootContainer = require('NavigationRootContainer');
3434
const React = require('react-native');
3535
const StyleSheet = require('StyleSheet');
3636
const Text = require('Text');
@@ -103,7 +103,7 @@ class NavigationHeader extends React.Component {
103103
);
104104
}
105105
_handleBackPress() {
106-
this.props.onNavigate(NavigationReducer.StackReducer.PopAction());
106+
this.props.onNavigate(NavigationRootContainer.getBackAction());
107107
}
108108
}
109109

Libraries/NavigationExperimental/NavigationStateUtils.js

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,10 @@ function indexOf(state: NavigationState, key: string): ?number {
6666
return index;
6767
}
6868

69-
function push(state: NavigationState, newChildState: NavigationState): NavigationState {
70-
const parentState = getParent(state);
71-
if (!parentState) {
72-
return state;
73-
}
74-
var lastChildren: Array<NavigationState> = parentState.children;
69+
function push(state: NavigationParentState, newChildState: NavigationState): NavigationParentState {
70+
var lastChildren: Array<NavigationState> = state.children;
7571
return {
76-
...parentState,
72+
...state,
7773
children: [
7874
...lastChildren,
7975
newChildState,
@@ -82,14 +78,10 @@ function push(state: NavigationState, newChildState: NavigationState): Navigatio
8278
};
8379
}
8480

85-
function pop(state: NavigationState): NavigationState {
86-
const parentState = getParent(state);
87-
if (!parentState) {
88-
return state;
89-
}
90-
const lastChildren = parentState.children;
81+
function pop(state: NavigationParentState): NavigationParentState {
82+
const lastChildren = state.children;
9183
return {
92-
...parentState,
84+
...state,
9385
children: lastChildren.slice(0, lastChildren.length - 1),
9486
index: lastChildren.length - 2,
9587
};

0 commit comments

Comments
 (0)