diff --git a/packages/core/src/__tests__/index.test.tsx b/packages/core/src/__tests__/index.test.tsx
index d9ec1a2b..8ebf9ab8 100644
--- a/packages/core/src/__tests__/index.test.tsx
+++ b/packages/core/src/__tests__/index.test.tsx
@@ -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));
@@ -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 }));
@@ -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,
});
});
@@ -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 = (
+
+
+
+
+
+ );
+
+ 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);
diff --git a/packages/core/src/types.tsx b/packages/core/src/types.tsx
index fc4b9661..37510661 100644
--- a/packages/core/src/types.tsx
+++ b/packages/core/src/types.tsx
@@ -377,6 +377,13 @@ export type NavigationProp<
dangerouslyGetParent():
| NavigationProp
| 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 &
PrivateValueStore;
diff --git a/packages/core/src/useNavigationCache.tsx b/packages/core/src/useNavigationCache.tsx
index a3e7101b..6a3943f7 100644
--- a/packages/core/src/useNavigationCache.tsx
+++ b/packages/core/src/useNavigationCache.tsx
@@ -43,10 +43,12 @@ 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 }),
// eslint-disable-next-line react-hooks/exhaustive-deps
- [getState, navigation, setOptions, router, emitter]
+ [getState, navigation, setOptions, router, emitter, parentNavigation]
);
const actions = {
@@ -54,8 +56,6 @@ export default function useNavigationCache<
...BaseActions,
};
- const parentNavigation = React.useContext(NavigationContext);
-
cache.current = state.routes.reduce>(
(acc, route, index) => {
const previous = cache.current[route.key];
@@ -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 => ({