Files
react-native/Libraries/NavigationExperimental/Reducer/NavigationTabsReducer.js
Eric Vicenti a3085464f6 NavigationExperimental
Summary:
A new API to unify internal navigation. Also addresses a highly-rated community 'pain': https://productpains.com/post/react-native/better-navigator-api-and-docs/

Offers the following improvements:

- Redux-style navigation logic is easy to reason about
- Navigation state can be easily saved and restored through refreshes
- Declarative navigation views can be implemented in native or JS
- Animations and gestures are isolated and now use the Animated library

public

Reviewed By: hedgerwang

Differential Revision: D2798048

fb-gh-sync-id: 88027ef9ead8a80afa38354252bc377455cc6dbb
2016-02-05 14:26:35 -08:00

144 lines
4.1 KiB
JavaScript

/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule NavigationTabsReducer
* @flow
*/
'use strict';
const NavigationFindReducer = require('NavigationFindReducer');
const NavigationStateUtils = require('NavigationState');
import type {
NavigationReducer,
NavigationReducerWithDefault,
NavigationState
} from 'NavigationState';
const ActionTypes = {
JUMP_TO: 'react-native/NavigationExperimental/tabs-jumpTo',
ON_TAB_ACTION: 'react-native/NavigationExperimental/tabs-onTabAction',
};
const DEFAULT_KEY = 'TABS_STATE_DEFAULT_KEY';
export type JumpToAction = {
type: typeof ActionTypes.JUMP_TO,
index: number,
};
function NavigationTabsJumpToAction(index: number): JumpToAction {
return {
type: ActionTypes.JUMP_TO,
index,
};
}
export type OnTabAction = {
type: string,
index: number,
action: any,
};
function NavigationTabsOnTabAction(index: number, action: any): OnTabAction {
return {
type: ActionTypes.ON_TAB_ACTION,
index,
action,
};
}
type TabsReducerConfig = {
key: string;
initialIndex: ?number;
tabReducers: Array<NavigationReducerWithDefault>;
};
function NavigationTabsReducer({key, initialIndex, tabReducers}: TabsReducerConfig): NavigationReducer {
if (initialIndex == null) {
initialIndex = 0;
}
if (key == null) {
key = DEFAULT_KEY;
}
return function(lastNavState: ?NavigationState, action: ?any): ?NavigationState {
if (!lastNavState) {
lastNavState = {
children: tabReducers.map(reducer => reducer(null, null)),
index: initialIndex,
key,
};
}
const lastParentNavState = NavigationStateUtils.getParent(lastNavState);
if (!action || !lastParentNavState) {
return lastNavState;
}
if (
action.type === ActionTypes.JUMP_TO &&
action.index !== lastParentNavState.index
) {
return NavigationStateUtils.jumpToIndex(
lastParentNavState,
action.index,
);
}
if (action.type === ActionTypes.ON_TAB_ACTION) {
const onTabAction: OnTabAction = action;
const lastTabRoute = lastParentNavState.children[onTabAction.index];
const tabReducer = tabReducers[onTabAction.index];
if (tabReducer) {
const newTabRoute = tabReducer(lastTabRoute, action.action);
if (newTabRoute && newTabRoute !== lastTabRoute) {
let navState = NavigationStateUtils.replaceAtIndex(
lastParentNavState,
onTabAction.index,
newTabRoute
);
navState = NavigationStateUtils.jumpToIndex(
navState,
onTabAction.index
);
return navState;
}
}
}
const subReducers = tabReducers.map((tabReducer, tabIndex) => {
return function reduceTab(lastTabState: ?NavigationState, tabAction: ?any): ?NavigationState {
if (!lastTabState) {
return tabReducer(lastTabState, tabAction);
}
if (!lastParentNavState) {
return lastTabState;
}
const lastSubTabState = lastParentNavState.children[tabIndex];
const nextSubTabState = tabReducer(lastSubTabState, tabAction);
if (nextSubTabState && lastSubTabState !== nextSubTabState) {
const tabs = lastParentNavState.children;
tabs[tabIndex] = nextSubTabState;
return {
...lastParentNavState,
tabs,
index: tabIndex,
};
}
return lastParentNavState;
};
});
let selectedTabReducer = subReducers.splice(lastParentNavState.index, 1)[0];
subReducers.unshift(selectedTabReducer);
const findReducer = NavigationFindReducer(subReducers);
if (findReducer) {
return findReducer(lastParentNavState, action);
}
return lastParentNavState;
};
}
NavigationTabsReducer.JumpToAction = NavigationTabsJumpToAction;
NavigationTabsReducer.OnTabAction = NavigationTabsOnTabAction;
module.exports = NavigationTabsReducer;