diff --git a/Examples/UIExplorer/NavigationExperimental/NavigationAnimatedExample.js b/Examples/UIExplorer/NavigationExperimental/NavigationAnimatedExample.js index 7b343f08d..a44d44c1e 100644 --- a/Examples/UIExplorer/NavigationExperimental/NavigationAnimatedExample.js +++ b/Examples/UIExplorer/NavigationExperimental/NavigationAnimatedExample.js @@ -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 { diff --git a/Examples/UIExplorer/NavigationExperimental/NavigationBasicExample.js b/Examples/UIExplorer/NavigationExperimental/NavigationBasicExample.js index 8f8669fb4..447ab2051 100644 --- a/Examples/UIExplorer/NavigationExperimental/NavigationBasicExample.js +++ b/Examples/UIExplorer/NavigationExperimental/NavigationBasicExample.js @@ -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({ { - onNavigate('page #' + navState.children.length); + onNavigate({ type: 'push', key: 'page #' + navState.children.length }); }} /> { - onNavigate(StackReducer.PopAction()); + onNavigate(NavigationRootContainer.getBackAction()); }} /> ({ 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 { }} /> { - this.props.onNavigate(ExampleNotifSettingsAction()); + this.props.onNavigate(ExampleNotifProfileAction()); }} /> { 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; } diff --git a/Examples/UIExplorer/UIExplorerNavigationReducer.js b/Examples/UIExplorer/UIExplorerNavigationReducer.js index e2e5ebede..3dee22c2c 100644 --- a/Examples/UIExplorer/UIExplorerNavigationReducer.js +++ b/Examples/UIExplorer/UIExplorerNavigationReducer.js @@ -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; diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationCard.js b/Libraries/CustomComponents/NavigationExperimental/NavigationCard.js index 8907a6e00..7021e73ab 100644 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationCard.js +++ b/Libraries/CustomComponents/NavigationExperimental/NavigationCard.js @@ -28,7 +28,7 @@ 'use strict'; const Animated = require('Animated'); -const NavigationReducer = require('NavigationReducer'); +const NavigationRootContainer = require('NavigationRootContainer'); const NavigationContainer = require('NavigationContainer'); const PanResponder = require('PanResponder'); const Platform = require('Platform'); @@ -95,7 +95,7 @@ class NavigationCard extends React.Component { const doesPop = (xRatio + vx) > 0.45; if (doesPop) { // todo: add an action which accepts velocity of the pop action/gesture, which is caught and used by NavigationAnimatedView - this.props.onNavigate(NavigationReducer.StackReducer.PopAction()); + this.props.onNavigate(NavigationRootContainer.getBackAction()); return; } Animated.spring(this.props.position, { diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationHeader.js b/Libraries/CustomComponents/NavigationExperimental/NavigationHeader.js index 0be15d189..82a807e2f 100644 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationHeader.js +++ b/Libraries/CustomComponents/NavigationExperimental/NavigationHeader.js @@ -30,7 +30,7 @@ const Animated = require('Animated'); const Image = require('Image'); const NavigationContainer = require('NavigationContainer'); -const NavigationReducer = require('NavigationReducer'); +const NavigationRootContainer = require('NavigationRootContainer'); const React = require('react-native'); const StyleSheet = require('StyleSheet'); const Text = require('Text'); @@ -103,7 +103,7 @@ class NavigationHeader extends React.Component { ); } _handleBackPress() { - this.props.onNavigate(NavigationReducer.StackReducer.PopAction()); + this.props.onNavigate(NavigationRootContainer.getBackAction()); } } diff --git a/Libraries/NavigationExperimental/NavigationStateUtils.js b/Libraries/NavigationExperimental/NavigationStateUtils.js index ba652ea87..c9d10ddfb 100644 --- a/Libraries/NavigationExperimental/NavigationStateUtils.js +++ b/Libraries/NavigationExperimental/NavigationStateUtils.js @@ -66,14 +66,10 @@ function indexOf(state: NavigationState, key: string): ?number { return index; } -function push(state: NavigationState, newChildState: NavigationState): NavigationState { - const parentState = getParent(state); - if (!parentState) { - return state; - } - var lastChildren: Array = parentState.children; +function push(state: NavigationParentState, newChildState: NavigationState): NavigationParentState { + var lastChildren: Array = state.children; return { - ...parentState, + ...state, children: [ ...lastChildren, newChildState, @@ -82,14 +78,10 @@ function push(state: NavigationState, newChildState: NavigationState): Navigatio }; } -function pop(state: NavigationState): NavigationState { - const parentState = getParent(state); - if (!parentState) { - return state; - } - const lastChildren = parentState.children; +function pop(state: NavigationParentState): NavigationParentState { + const lastChildren = state.children; return { - ...parentState, + ...state, children: lastChildren.slice(0, lastChildren.length - 1), index: lastChildren.length - 2, }; diff --git a/Libraries/NavigationExperimental/Reducer/NavigationStackReducer.js b/Libraries/NavigationExperimental/Reducer/NavigationStackReducer.js index 624d822e6..89d96da09 100644 --- a/Libraries/NavigationExperimental/Reducer/NavigationStackReducer.js +++ b/Libraries/NavigationExperimental/Reducer/NavigationStackReducer.js @@ -15,6 +15,7 @@ var NavigationStateUtils = require('NavigationStateUtils'); import type { NavigationState, + NavigationParentState, NavigationReducer, } from 'NavigationStateUtils'; @@ -26,121 +27,81 @@ export type NavigationStackReducerAction = BackAction | { type: string, }; -const ActionTypes = { - PUSH: 'react-native/NavigationExperimental/stack-push', - POP: 'react-native/NavigationExperimental/stack-pop', - JUMP_TO: 'react-native/NavigationExperimental/stack-jumpTo', - JUMP_TO_INDEX: 'react-native/NavigationExperimental/stack-jumpToIndex', - RESET: 'react-native/NavigationExperimental/stack-reset', +export type ReducerForStateHandler = (state: NavigationState) => NavigationReducer; + +export type PushedReducerForActionHandler = (action: any) => ?NavigationReducer; + +export type StackReducerConfig = { + /* + * The initialState is that the reducer will use when there is no previous state. + * Must be a NavigationParentState: + * + * { + * children: [ + * {key: 'subState0'}, + * {key: 'subState1'}, + * ], + * index: 0, + * key: 'navStackKey' + * } + */ + initialState: NavigationParentState; + + /* + * Returns the sub-reducer for a particular state to handle. This will be called + * when we need to handle an action on a sub-state. If no reducer is returned, + * no action will be taken + */ + getReducerForState?: ReducerForStateHandler; + + /* + * Returns a sub-reducer that will be used when pushing a new route. If a reducer + * is returned, it be called to get the new state that will be pushed + */ + getPushedReducerForAction: PushedReducerForActionHandler; }; -const DEFAULT_KEY = 'NAV_STACK_DEFAULT_KEY'; +const defaultGetReducerForState = (initialState) => (state) => state || initialState; -function NavigationStackPushAction(state: NavigationState): NavigationStackReducerAction { - return { - type: ActionTypes.PUSH, - state, - }; -} - -function NavigationStackPopAction(): NavigationStackReducerAction { - return { - type: ActionTypes.POP, - }; -} - -function NavigationStackJumpToAction(key: string): NavigationStackReducerAction { - return { - type: ActionTypes.JUMP_TO, - key, - }; -} - -function NavigationStackJumpToIndexAction(index: number): NavigationStackReducerAction { - return { - type: ActionTypes.JUMP_TO_INDEX, - index, - }; -} - -function NavigationStackResetAction(children: Array, index: number): NavigationStackReducerAction { - return { - type: ActionTypes.RESET, - index, - children, - }; -} - -type StackReducerConfig = { - initialStates: Array; - initialIndex: ?number; - key: ?string; - matchAction: (action: any) => boolean; - actionStateMap: (action: any) => NavigationState; -}; - -function NavigationStackReducer({initialStates, initialIndex, key, matchAction, actionStateMap}: StackReducerConfig): NavigationReducer { +function NavigationStackReducer({initialState, getReducerForState, getPushedReducerForAction}: StackReducerConfig): NavigationReducer { + const getReducerForStateWithDefault = getReducerForState || defaultGetReducerForState; return function (lastState: ?NavigationState, action: any): NavigationState { - if (key == null) { - key = DEFAULT_KEY; - } - if (initialIndex == null) { - initialIndex = initialStates.length - 1; - } if (!lastState) { - lastState = { - index: initialIndex, - children: initialStates, - key, - }; + return initialState; } const lastParentState = NavigationStateUtils.getParent(lastState); - if (!action || !lastParentState) { + if (!lastParentState) { return lastState; } switch (action.type) { - case ActionTypes.PUSH: - return NavigationStateUtils.push( - lastParentState, - action.state - ); - case ActionTypes.POP: case 'BackAction': if (lastParentState.index === 0 || lastParentState.children.length === 1) { return lastParentState; } return NavigationStateUtils.pop(lastParentState); - case ActionTypes.JUMP_TO: - return NavigationStateUtils.jumpTo( - lastParentState, - action.key - ); - case ActionTypes.JUMP_TO_INDEX: - return NavigationStateUtils.jumpToIndex( - lastParentState, - action.index - ); - case ActionTypes.RESET: - return { - ...lastParentState, - index: action.index, - children: action.children, - }; } - if (matchAction(action)) { + + const activeSubState = lastParentState.children[lastParentState.index]; + const activeSubReducer = getReducerForStateWithDefault(activeSubState); + const nextActiveState = activeSubReducer(activeSubState, action); + if (nextActiveState !== activeSubState) { + const nextChildren = [...lastParentState.children]; + nextChildren[lastParentState.index] = nextActiveState; + return { + ...lastParentState, + children: nextChildren, + }; + } + + const subReducerToPush = getPushedReducerForAction(action); + if (subReducerToPush) { return NavigationStateUtils.push( lastParentState, - actionStateMap(action) + subReducerToPush(null, action) ); } return lastParentState; }; } -NavigationStackReducer.PushAction = NavigationStackPushAction; -NavigationStackReducer.PopAction = NavigationStackPopAction; -NavigationStackReducer.JumpToAction = NavigationStackJumpToAction; -NavigationStackReducer.JumpToIndexAction = NavigationStackJumpToIndexAction; -NavigationStackReducer.ResetAction = NavigationStackResetAction; - module.exports = NavigationStackReducer; diff --git a/Libraries/NavigationExperimental/Reducer/NavigationTabsReducer.js b/Libraries/NavigationExperimental/Reducer/NavigationTabsReducer.js index 3f849300b..eaf9ef83b 100644 --- a/Libraries/NavigationExperimental/Reducer/NavigationTabsReducer.js +++ b/Libraries/NavigationExperimental/Reducer/NavigationTabsReducer.js @@ -24,8 +24,6 @@ const ActionTypes = { JUMP_TO: 'react-native/NavigationExperimental/tabs-jumpTo', }; -const DEFAULT_KEY = 'TABS_STATE_DEFAULT_KEY'; - export type JumpToAction = { type: typeof ActionTypes.JUMP_TO, index: number, @@ -44,9 +42,6 @@ type TabsReducerConfig = { }; function NavigationTabsReducer({key, initialIndex, tabReducers}: TabsReducerConfig): NavigationReducer { - if (key == null) { - key = DEFAULT_KEY; - } return function(lastNavState: ?NavigationState, action: ?any): NavigationState { if (!lastNavState) { lastNavState = { @@ -89,16 +84,16 @@ function NavigationTabsReducer({key, initialIndex, tabReducers}: TabsReducerConf }; }); let selectedTabReducer = subReducers.splice(lastParentNavState.index, 1)[0]; - subReducers.unshift(selectedTabReducer); - subReducers.push(function(navState: ?NavigationState, action: any): NavigationState { + subReducers.unshift(function(navState: ?NavigationState, action: any): NavigationState { if (navState && action.type === 'BackAction') { return NavigationStateUtils.jumpToIndex( lastParentNavState, - 0 + initialIndex || 0 ); } return lastParentNavState; }); + subReducers.unshift(selectedTabReducer); const findReducer = NavigationFindReducer(subReducers, lastParentNavState); return findReducer(lastParentNavState, action); }; diff --git a/Libraries/NavigationExperimental/Reducer/__tests__/NavigationStackReducer-test.js b/Libraries/NavigationExperimental/Reducer/__tests__/NavigationStackReducer-test.js index 4d497346c..400057ab0 100644 --- a/Libraries/NavigationExperimental/Reducer/__tests__/NavigationStackReducer-test.js +++ b/Libraries/NavigationExperimental/Reducer/__tests__/NavigationStackReducer-test.js @@ -15,183 +15,90 @@ jest .mock('ErrorUtils'); const NavigationStackReducer = require('NavigationStackReducer'); - -const { - JumpToAction, - JumpToIndexAction, - PopAction, - PushAction, - ResetAction, -} = NavigationStackReducer; +const NavigationRootContainer = require('NavigationRootContainer'); describe('NavigationStackReducer', () => { - it('handles PushAction', () => { - const initialStates = [ - {key: 'route0'}, - {key: 'route1'}, - ]; - let reducer = NavigationStackReducer({ - initialStates, - matchAction: () => true, - actionStateMap: (action) => action, - }); - - let state = reducer(); - expect(state.children).toBe(initialStates); - expect(state.index).toBe(1); - expect(state.key).toBe('NAV_STACK_DEFAULT_KEY'); - - state = reducer(state, PushAction({key: 'route2'})); - expect(state.children[0].key).toBe('route0'); - expect(state.children[1].key).toBe('route1'); - expect(state.children[2].key).toBe('route2'); - expect(state.index).toBe(2); - }); - - it('handles PopAction', () => { - let reducer = NavigationStackReducer({ - initialStates: [ + it('provides default/initial state', () => { + const initialState = { + children: [ {key: 'a'}, - {key: 'b'}, ], - initialIndex: 1, + index: 0, key: 'myStack', - matchAction: () => true, - actionStateMap: (action) => action, + }; + const reducer = NavigationStackReducer({ + getPushedReducerForAction: (action) => null, + getReducerForState: (state) => () => state, + initialState, }); - - let state = reducer(); - expect(state.children[0].key).toBe('a'); - expect(state.children[1].key).toBe('b'); - expect(state.children.length).toBe(2); - expect(state.index).toBe(1); - expect(state.key).toBe('myStack'); - - state = reducer(state, PopAction()); - expect(state.children[0].key).toBe('a'); - expect(state.children.length).toBe(1); - expect(state.index).toBe(0); - expect(state.key).toBe('myStack'); - - // make sure Pop on an single-route state is a no-op - state = reducer(state, PopAction()); - expect(state.children[0].key).toBe('a'); - expect(state.children.length).toBe(1); - expect(state.index).toBe(0); - expect(state.key).toBe('myStack'); + const dummyAction = {type: 'dummyAction'}; + expect(reducer(null, dummyAction)).toBe(initialState); }); - it('handles JumpToAction', () => { - let reducer = NavigationStackReducer({ - initialStates: [ - {key: 'a'}, - {key: 'b'}, - {key: 'c'}, - ], - initialIndex: 0, - key: 'myStack', - matchAction: () => true, - actionStateMap: (action) => action, + it('handles basic reducer pushing', () => { + const reducer = NavigationStackReducer({ + getPushedReducerForAction: (action) => { + if (action.type === 'TestPushAction') { + return (state) => state || {key: action.testValue}; + } + return null; + }, + getReducerForState: (state) => () => state, + initialState: { + children: [ + {key: 'first'}, + ], + index: 0, + key: 'myStack' + } }); + const state1 = reducer(null, {type: 'default'}); + expect(state1.children.length).toBe(1); + expect(state1.children[0].key).toBe('first'); + expect(state1.index).toBe(0); - let state = reducer(); - expect(state.children[0].key).toBe('a'); - expect(state.children[1].key).toBe('b'); - expect(state.children[2].key).toBe('c'); - expect(state.children.length).toBe(3); - expect(state.index).toBe(0); - - state = reducer(state, JumpToAction('b')); - expect(state.children[0].key).toBe('a'); - expect(state.children[1].key).toBe('b'); - expect(state.children[2].key).toBe('c'); - expect(state.children.length).toBe(3); - expect(state.index).toBe(1); - - state = reducer(state, JumpToAction('c')); - expect(state.children[0].key).toBe('a'); - expect(state.children[1].key).toBe('b'); - expect(state.children[2].key).toBe('c'); - expect(state.children.length).toBe(3); - expect(state.index).toBe(2); - - state = reducer(state, JumpToAction('c')); - expect(state.children[0].key).toBe('a'); - expect(state.children[1].key).toBe('b'); - expect(state.children[2].key).toBe('c'); - expect(state.children.length).toBe(3); - expect(state.index).toBe(2); - expect(state.key).toBe('myStack'); + const action = {type: 'TestPushAction', testValue: 'second'}; + const state2 = reducer(state1, action); + expect(state2.children.length).toBe(2); + expect(state2.children[0].key).toBe('first'); + expect(state2.children[1].key).toBe('second'); + expect(state2.index).toBe(1); }); - it('handles JumpToIndexAction', () => { - let reducer = NavigationStackReducer({ - initialStates: [ - {key: 'a'}, - {key: 'b'}, - {key: 'c'}, - ], - initialIndex: 2, - key: 'myStack', - matchAction: () => true, - actionStateMap: (action) => action, + it('handles BackAction', () => { + const reducer = NavigationStackReducer({ + getPushedReducerForAction: (action) => { + if (action.type === 'TestPushAction') { + return (state) => state || {key: action.testValue}; + } + return null; + }, + getReducerForState: (state) => () => state, + initialState: { + children: [ + {key: 'a'}, + {key: 'b'}, + ], + index: 1, + key: 'myStack', + }, }); - let state = reducer(); - expect(state.children.length).toBe(3); - expect(state.index).toBe(2); + const state1 = reducer(null, {type: 'MyDefaultAction'}); + expect(state1.children[0].key).toBe('a'); + expect(state1.children[1].key).toBe('b'); + expect(state1.children.length).toBe(2); + expect(state1.index).toBe(1); + expect(state1.key).toBe('myStack'); - state = reducer(state, JumpToIndexAction(0)); - expect(state.children.length).toBe(3); - expect(state.index).toBe(0); + const state2 = reducer(state1, NavigationRootContainer.getBackAction()); + expect(state2.children[0].key).toBe('a'); + expect(state2.children.length).toBe(1); + expect(state2.index).toBe(0); - state = reducer(state, JumpToIndexAction(1)); - expect(state.children[0].key).toBe('a'); - expect(state.children[1].key).toBe('b'); - expect(state.children[2].key).toBe('c'); - expect(state.children.length).toBe(3); - expect(state.index).toBe(1); - expect(state.key).toBe('myStack'); + const state3 = reducer(state2, NavigationRootContainer.getBackAction()); + expect(state3).toBe(state2); }); - it('handles ResetAction', () => { - let reducer = NavigationStackReducer({ - initialStates: [ - {key: 'a'}, - {key: 'b'}, - ], - initialIndex: 1, - key: 'myStack', - matchAction: () => true, - actionStateMap: (action) => action, - }); - - let state = reducer(); - expect(state.children[0].key).toBe('a'); - expect(state.children[1].key).toBe('b'); - expect(state.children.length).toBe(2); - expect(state.index).toBe(1); - - state = reducer(state, ResetAction([{key: 'c'}, {key: 'd'}], 0)); - expect(state.children[0].key).toBe('c'); - expect(state.children[1].key).toBe('d'); - expect(state.children.length).toBe(2); - expect(state.index).toBe(0); - - const newStates = [ - {key: 'e'}, - {key: 'f'}, - {key: 'g'}, - ]; - - state = reducer(state, ResetAction(newStates, 1)); - expect(state.children[0].key).toBe('e'); - expect(state.children[1].key).toBe('f'); - expect(state.children[2].key).toBe('g'); - expect(state.children.length).toBe(3); - expect(state.index).toBe(1); - expect(state.key).toBe('myStack'); - }); - -}); +}); \ No newline at end of file