Skip to content

Commit

Permalink
refactor: drop getStateForChildUpdate in favor of getStateForRouteFoc…
Browse files Browse the repository at this point in the history
…us (facebook#15)
  • Loading branch information
satya164 authored and osdnk committed Jul 20, 2019
1 parent 3d8ba13 commit 44b2ace
Show file tree
Hide file tree
Showing 12 changed files with 269 additions and 306 deletions.
35 changes: 14 additions & 21 deletions example/StackNavigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,20 @@ const StackRouter: Router<CommonAction | Action> = {
};
},

getStateForRouteFocus(state, key) {
const index = state.routes.findIndex(r => r.key === key);

if (index === -1 || index === state.index) {
return state;
}

return {
...state,
index,
routes: state.routes.slice(0, index + 1),
};
},

getStateForAction(state, action) {
switch (action.type) {
case 'PUSH':
Expand Down Expand Up @@ -219,27 +233,6 @@ const StackRouter: Router<CommonAction | Action> = {
}
},

getStateForChildUpdate(state, { update, focus, key }) {
const index = state.routes.findIndex(r => r.key === key);

if (index === -1) {
return state;
}

return {
...state,
index: focus ? index : state.index,
routes: focus
? [
...state.routes.slice(0, index),
{ ...state.routes[index], state: update },
]
: state.routes.map((route, i) =>
i === index ? { ...route, state: update } : route
),
};
},

shouldActionPropagateToChildren(action) {
return action.type === 'NAVIGATE';
},
Expand Down
26 changes: 10 additions & 16 deletions example/TabNavigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,16 @@ const TabRouter: Router<Action | CommonAction> = {
};
},

getStateForRouteFocus(state, key) {
const index = state.routes.findIndex(r => r.key === key);

if (index === -1) {
return state;
}

return { ...state, index };
},

getStateForAction(state, action) {
switch (action.type) {
case 'JUMP_TO':
Expand Down Expand Up @@ -152,22 +162,6 @@ const TabRouter: Router<Action | CommonAction> = {
}
},

getStateForChildUpdate(state, { update, focus, key }) {
const index = state.routes.findIndex(r => r.key === key);

if (index === -1) {
return state;
}

return {
...state,
index: focus ? index : state.index,
routes: state.routes.map((route, i) =>
i === index ? { ...route, state: update } : route
),
};
},

shouldActionPropagateToChildren(action) {
return action.type === 'NAVIGATE';
},
Expand Down
8 changes: 2 additions & 6 deletions src/NavigationBuilderContext.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { NavigationHelpers, NavigationAction, NavigationState } from './types';
import { NavigationHelpers, NavigationAction } from './types';

export type ChildActionListener = (
action: NavigationAction,
Expand All @@ -11,11 +11,7 @@ const NavigationBuilderContext = React.createContext<{
onAction?: (action: NavigationAction, sourceNavigatorKey?: string) => boolean;
addActionListener?: (listener: ChildActionListener) => void;
removeActionListener?: (listener: ChildActionListener) => void;
onChildUpdate?: (
state: NavigationState,
focus: boolean,
key: string | undefined
) => void;
onRouteFocus?: (key: string) => void;
}>({});

export default NavigationBuilderContext;
81 changes: 81 additions & 0 deletions src/__tests__/__fixtures__/MockRouter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { Router } from '../../types';

const MockRouter: Router<{ type: string }> & { key: number } = {
key: 0,

getInitialState({
routeNames,
initialRouteName = routeNames[0],
initialParamsList,
}) {
const index = routeNames.indexOf(initialRouteName);

return {
key: String(MockRouter.key++),
index,
routeNames,
routes: routeNames.map(name => ({
name,
key: name,
params: initialParamsList[name],
})),
};
},

getRehydratedState({ routeNames, partialState }) {
let state = partialState;

if (state.routeNames === undefined || state.key === undefined) {
state = {
...state,
routeNames,
key: String(MockRouter.key++),
};
}

return state;
},

getStateForRouteNamesChange(state, { routeNames }) {
return {
...state,
routeNames,
routes: state.routes.filter(route => routeNames.includes(route.name)),
};
},

getStateForRouteFocus(state, key) {
const index = state.routes.findIndex(r => r.key === key);

if (index === -1 || index === state.index) {
return state;
}

return { ...state, index };
},

getStateForAction(state, action) {
switch (action.type) {
case 'UPDATE':
return { ...state };

case 'NOOP':
return state;

default:
return null;
}
},

shouldActionPropagateToChildren() {
return false;
},

shouldActionChangeFocus() {
return false;
},

actionCreators: {},
};

export default MockRouter;
155 changes: 1 addition & 154 deletions src/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,91 +3,7 @@ import { render, act } from 'react-native-testing-library';
import Screen from '../Screen';
import NavigationContainer from '../NavigationContainer';
import useNavigationBuilder from '../useNavigationBuilder';
import { Router } from '../types';

export const MockRouter: Router<{ type: string }> & { key: number } = {
key: 0,

getInitialState({
routeNames,
initialRouteName = routeNames[0],
initialParamsList,
}) {
const index = routeNames.indexOf(initialRouteName);

return {
key: String(MockRouter.key++),
index,
routeNames,
routes: routeNames.map(name => ({
name,
key: name,
params: initialParamsList[name],
})),
};
},

getRehydratedState({ routeNames, partialState }) {
let state = partialState;

if (state.routeNames === undefined || state.key === undefined) {
state = {
...state,
routeNames,
key: String(MockRouter.key++),
};
}

return state;
},

getStateForRouteNamesChange(state, { routeNames }) {
return {
...state,
routeNames,
routes: state.routes.filter(route => routeNames.includes(route.name)),
};
},

getStateForAction(state, action) {
switch (action.type) {
case 'UPDATE':
return { ...state };

case 'NOOP':
return state;

default:
return null;
}
},

getStateForChildUpdate(state, { update, focus, key }) {
const index = state.routes.findIndex(r => r.key === key);

if (index === -1) {
return state;
}

return {
...state,
index: focus ? index : state.index,
routes: state.routes.map((route, i) =>
i === index ? { ...route, state: update } : route
),
};
},

shouldActionPropagateToChildren() {
return false;
},

shouldActionChangeFocus() {
return false;
},

actionCreators: {},
};
import MockRouter from './__fixtures__/MockRouter';

beforeEach(() => (MockRouter.key = 0));

Expand Down Expand Up @@ -399,75 +315,6 @@ it('cleans up state when the navigator unmounts', () => {
expect(onStateChange).lastCalledWith(undefined);
});

it("lets parent handle the action if child didn't", () => {
const ParentRouter: Router<{ type: string }> = {
...MockRouter,

getStateForAction(state, action) {
if (action.type === 'REVERSE') {
return {
...state,
routes: state.routes.slice().reverse(),
};
}

return MockRouter.getStateForAction(state, action);
},
};

const ParentNavigator = (props: any) => {
const { state, descriptors } = useNavigationBuilder(ParentRouter, props);

return descriptors[state.routes[state.index].key].render();
};

const ChildNavigator = (props: any) => {
const { state, descriptors } = useNavigationBuilder(MockRouter, props);

return descriptors[state.routes[state.index].key].render();
};

const TestScreen = (props: any) => {
React.useEffect(() => {
props.navigation.dispatch({ type: 'REVERSE' });

// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

return null;
};

const onStateChange = jest.fn();

render(
<NavigationContainer onStateChange={onStateChange}>
<ParentNavigator initialRouteName="baz">
<Screen name="foo">{() => null}</Screen>
<Screen name="bar">{() => null}</Screen>
<Screen name="baz">
{() => (
<ChildNavigator>
<Screen name="qux" component={TestScreen} />
</ChildNavigator>
)}
</Screen>
</ParentNavigator>
</NavigationContainer>
);

expect(onStateChange).toBeCalledTimes(1);
expect(onStateChange).lastCalledWith({
index: 2,
key: '0',
routeNames: ['foo', 'bar', 'baz'],
routes: [
{ key: 'baz', name: 'baz' },
{ key: 'bar', name: 'bar' },
{ key: 'foo', name: 'foo' },
],
});
});

it('allows arbitrary state updates by dispatching a function', () => {
const TestNavigator = (props: any) => {
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
Expand Down
Loading

0 comments on commit 44b2ace

Please sign in to comment.