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
This commit is contained in:
Eric Vicenti
2016-02-22 16:15:42 -08:00
committed by facebook-github-bot-6
parent 876ecb291f
commit dcb68db758
10 changed files with 253 additions and 369 deletions

View File

@@ -29,11 +29,20 @@ var {
} = NavigationExperimental;
const NavigationBasicReducer = NavigationReducer.StackReducer({
initialStates: [
{key: 'First Route'}
],
matchAction: action => action.type === 'push',
actionStateMap: action => ({key: action.key}),
getPushedReducerForAction: (action) => {
if (action.type === 'push') {
return (state) => state || {key: action.key};
}
return null;
},
getReducerForState: (initialState) => (state) => state || initialState,
initialState: {
key: 'AnimatedExampleStackKey',
index: 0,
children: [
{key: 'First Route'},
],
},
});
class NavigationAnimatedExample extends React.Component {

View File

@@ -26,12 +26,21 @@ const {
} = NavigationExperimental;
const StackReducer = NavigationReducer.StackReducer;
const NavigationBasicReducer = StackReducer({
initialStates: [
{key: 'first_page'}
],
matchAction: action => true,
actionStateMap: action => ({key: action}),
const NavigationBasicReducer = NavigationReducer.StackReducer({
getPushedReducerForAction: (action) => {
if (action.type === 'push') {
return (state) => state || {key: action.key};
}
return null;
},
getReducerForState: (initialState) => (state) => state || initialState,
initialState: {
key: 'BasicExampleStackKey',
index: 0,
children: [
{key: 'First Route'},
],
},
});
const NavigationBasicExample = React.createClass({
@@ -51,13 +60,13 @@ const NavigationBasicExample = React.createClass({
<NavigationExampleRow
text={`Push page #${navState.children.length}`}
onPress={() => {
onNavigate('page #' + navState.children.length);
onNavigate({ type: 'push', key: 'page #' + navState.children.length });
}}
/>
<NavigationExampleRow
text="pop"
onPress={() => {
onNavigate(StackReducer.PopAction());
onNavigate(NavigationRootContainer.getBackAction());
}}
/>
<NavigationExampleRow

View File

@@ -10,7 +10,9 @@
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
*
* @flow
*/
'use strict';
const React = require('react-native');
@@ -32,6 +34,8 @@ const {
const NavigationExampleRow = require('./NavigationExampleRow');
const NavigationExampleTabBar = require('./NavigationExampleTabBar');
import type {NavigationParentState} from 'NavigationStateUtils';
const ExampleExitAction = () => ({
isExitAction: true,
});
@@ -39,25 +43,25 @@ ExampleExitAction.match = (action) => (
action && action.isExitAction === true
);
const ExamplePageAction = (type) => ({
const PageAction = (type) => ({
type,
isPageAction: true,
});
ExamplePageAction.match = (action) => (
PageAction.match = (action) => (
action && action.isPageAction === true
);
const ExampleSettingsPageAction = (type) => ({
...ExamplePageAction(type),
isSettingsPageAction: true,
const ExampleProfilePageAction = (type) => ({
...PageAction(type),
isProfilePageAction: true,
});
ExampleSettingsPageAction.match = (action) => (
action && action.isSettingsPageAction === true
ExampleProfilePageAction.match = (action) => (
action && action.isProfilePageAction === true
);
const ExampleInfoAction = () => ExamplePageAction('InfoPage');
const ExampleInfoAction = () => PageAction('InfoPage');
const ExampleNotifSettingsAction = () => ExampleSettingsPageAction('NotifSettingsPage');
const ExampleNotifProfileAction = () => ExampleProfilePageAction('NotifProfilePage');
const _jsInstanceUniqueId = '' + Date.now();
let _uniqueIdCount = 0;
@@ -68,70 +72,70 @@ function pageStateActionMap(action) {
};
}
function getTabActionMatcher(key) {
return function (action) {
if (!ExamplePageAction.match(action)) {
return false;
}
if (ExampleSettingsPageAction.match(action)) {
return key === 'settings';
}
return true;
};
}
var ExampleTabs = [
{
label: 'Account',
reducer: NavigationReducer.StackReducer({
initialStates: [
{type: 'AccountPage', key: 'base'}
],
key: 'account',
matchAction: getTabActionMatcher('account'),
actionStateMap: pageStateActionMap,
}),
},
{
label: 'Notifications',
reducer: NavigationReducer.StackReducer({
initialStates: [
{type: 'NotifsPage', key: 'base'}
],
key: 'notifs',
matchAction: getTabActionMatcher('notifs'),
actionStateMap: pageStateActionMap,
}),
},
{
label: 'Settings',
reducer: NavigationReducer.StackReducer({
initialStates: [
{type: 'SettingsPage', key: 'base'}
],
key: 'settings',
matchAction: getTabActionMatcher('settings'),
actionStateMap: pageStateActionMap,
}),
},
];
const ExampleAppReducer = NavigationReducer.TabsReducer({
tabReducers: ExampleTabs.map(tab => tab.reducer),
key: 'AppNavigationState',
initialIndex: 0,
tabReducers: [
NavigationReducer.StackReducer({
getPushedReducerForAction: (action) => {
if (PageAction.match(action) && !ExampleProfilePageAction.match(action)) {
return (state) => (state || pageStateActionMap(action));
}
return null;
},
initialState: {
key: 'notifs',
index: 0,
children: [
{key: 'base', type: 'NotifsPage'},
],
},
}),
NavigationReducer.StackReducer({
getPushedReducerForAction: (action) => {
if (PageAction.match(action) && !ExampleProfilePageAction.match(action)) {
return (state) => (state || pageStateActionMap(action));
}
return null;
},
initialState: {
key: 'settings',
index: 0,
children: [
{key: 'base', type: 'SettingsPage'},
],
},
}),
NavigationReducer.StackReducer({
getPushedReducerForAction: (action) => {
if (PageAction.match(action) || ExampleProfilePageAction.match(action)) {
return (state) => (state || pageStateActionMap(action));
}
return null;
},
initialState: {
key: 'profile',
index: 0,
children: [
{key: 'base', type: 'ProfilePage'},
],
},
}),
],
});
function stateTypeTitleMap(pageState) {
switch (pageState.type) {
case 'AccountPage':
return 'Account Page';
case 'ProfilePage':
return 'Profile Page';
case 'NotifsPage':
return 'Notifications';
case 'SettingsPage':
return 'Settings';
case 'InfoPage':
return 'Info Page';
case 'NotifSettingsPage':
return 'Notification Settings';
case 'NotifProfilePage':
return 'Page in Profile';
}
}
@@ -173,9 +177,9 @@ class ExampleTabScreen extends React.Component {
}}
/>
<NavigationExampleRow
text="Open notifs settings in settings tab"
text="Open a page in the profile tab"
onPress={() => {
this.props.onNavigate(ExampleNotifSettingsAction());
this.props.onNavigate(ExampleNotifProfileAction());
}}
/>
<NavigationExampleRow
@@ -196,19 +200,19 @@ class NavigationCompositionExample extends React.Component {
return (
<NavigationRootContainer
reducer={ExampleAppReducer}
persistenceKey="NavigationCompositionExampleState"
persistenceKey="NavigationCompositionState"
ref={navRootContainer => { this.navRootContainer = navRootContainer; }}
renderNavigation={this.renderApp.bind(this)}
/>
);
}
handleBackAction() {
handleBackAction(): boolean {
return (
this.navRootContainer &&
this.navRootContainer.handleNavigation(NavigationRootContainer.getBackAction())
);
}
renderApp(navigationState, onNavigate) {
renderApp(navigationState: NavigationParentState, onNavigate: Function) {
if (!navigationState) {
return null;
}

View File

@@ -36,13 +36,20 @@ export type UIExplorerNavigationState = {
};
const UIExplorerStackReducer = StackReducer({
key: 'UIExplorerMainStack',
initialStates: [
{key: 'AppList'},
],
initialIndex: 0,
matchAction: action => action.openExample && !!UIExplorerList.Modules[action.openExample],
actionStateMap: action => ({ key: action.openExample, }),
getPushedReducerForAction: (action) => {
if (action.type === 'UIExplorerExampleAction' && UIExplorerList.Modules[action.openExample]) {
return (state) => state || {key: action.openExample};
}
return null;
},
getReducerForState: (initialState) => (state) => state || initialState,
initialState: {
key: 'UIExplorerMainStack',
index: 0,
children: [
{key: 'AppList'},
],
},
});
function UIExplorerNavigationReducer(lastState: ?UIExplorerNavigationState, action: any): UIExplorerNavigationState {
@@ -86,7 +93,7 @@ function UIExplorerNavigationReducer(lastState: ?UIExplorerNavigationState, acti
if (newStack !== lastState.stack) {
return {
externalExample: null,
stack: UIExplorerStackReducer(null, action),
stack: newStack,
}
}
return lastState;