Skip to content
This repository has been archived by the owner on Feb 8, 2020. It is now read-only.

Commit

Permalink
feat: add dangerouslyGetState (#63)
Browse files Browse the repository at this point in the history
  • Loading branch information
osdnk authored Aug 21, 2019
1 parent c0045d8 commit f7ff0c1
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 3 deletions.
38 changes: 38 additions & 0 deletions packages/core/src/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import Screen from '../Screen';
import NavigationContainer from '../NavigationContainer';
import useNavigationBuilder from '../useNavigationBuilder';
import MockRouter, { MockRouterKey } from './__fixtures__/MockRouter';
import useNavigation from '../useNavigation';
import { NavigationState } from '../types';

beforeEach(() => (MockRouterKey.current = 0));

Expand Down Expand Up @@ -467,6 +469,7 @@ it('updates route params with setParams applied to parent', () => {
{ key: 'foo', name: 'foo', params: { username: 'alice' } },
{ key: 'bar', name: 'bar' },
],
stale: false,
});

act(() => setParams({ age: 25 }));
Expand All @@ -480,6 +483,7 @@ it('updates route params with setParams applied to parent', () => {
{ key: 'foo', name: 'foo', params: { username: 'alice', age: 25 } },
{ key: 'bar', name: 'bar' },
],
stale: false,
});
});

Expand Down Expand Up @@ -519,6 +523,40 @@ it('handles change in route names', () => {
});
});

it('gives access to internal state', () => {
const TestNavigator = (props: any): any => {
const { state, descriptors } = useNavigationBuilder(MockRouter, props);

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

let state: NavigationState | undefined;

const Test = () => {
const navigation = useNavigation();
state = navigation.dangerouslyGetState();
return null;
};

const root = (
<NavigationContainer>
<TestNavigator initialRouteName="bar">
<Screen name="bar" component={Test} />
</TestNavigator>
</NavigationContainer>
);

render(root).update(root);

expect(state).toEqual({
index: 0,
key: '0',
routeNames: ['bar'],
routes: [{ key: 'bar', name: 'bar' }],
stale: false,
});
});

it("throws if navigator doesn't have any screens", () => {
const TestNavigator = (props: any) => {
useNavigationBuilder(MockRouter, props);
Expand Down
7 changes: 7 additions & 0 deletions packages/core/src/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,13 @@ export type NavigationProp<
dangerouslyGetParent():
| NavigationProp<ParamListBase, string, any, any>
| undefined;

/**
* Returns the navigator's state. Reason why the function is called
* dangerouslyGetState is to discourage developers to use internal navigation's state.
* Note that this method doesn't re-render screen when the result changes. So don't use it in `render`.
*/
dangerouslyGetState(): State;
} & EventConsumer<EventMap & EventMapBase> &
PrivateValueStore<ParamList, RouteName, EventMap>;

Expand Down
7 changes: 4 additions & 3 deletions packages/core/src/useNavigationCache.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,19 @@ export default function useNavigationCache<
// Cache object which holds navigation objects for each screen
// We use `React.useMemo` instead of `React.useRef` coz we want to invalidate it when deps change
// In reality, these deps will rarely change, if ever
const parentNavigation = React.useContext(NavigationContext);

const cache = React.useMemo(
() => ({ current: {} as NavigationCache<State, ScreenOptions> }),
// eslint-disable-next-line react-hooks/exhaustive-deps
[getState, navigation, setOptions, router, emitter]
[getState, navigation, setOptions, router, emitter, parentNavigation]
);

const actions = {
...router.actionCreators,
...BaseActions,
};

const parentNavigation = React.useContext(NavigationContext);

cache.current = state.routes.reduce<NavigationCache<State, ScreenOptions>>(
(acc, route, index) => {
const previous = cache.current[route.key];
Expand Down Expand Up @@ -92,6 +92,7 @@ export default function useNavigationCache<
...helpers,
...emitter.create(route.key),
dangerouslyGetParent: () => parentNavigation,
dangerouslyGetState: getState as () => State,
dispatch,
setOptions: (options: object) =>
setOptions(o => ({
Expand Down

0 comments on commit f7ff0c1

Please sign in to comment.