Skip to content

Commit

Permalink
feat: add devtools package
Browse files Browse the repository at this point in the history
  • Loading branch information
satya164 committed Jun 15, 2020
1 parent e000138 commit 0a4ba5c
Show file tree
Hide file tree
Showing 17 changed files with 491 additions and 173 deletions.
3 changes: 2 additions & 1 deletion .eslintrc.json
Expand Up @@ -11,7 +11,8 @@
"@react-navigation/drawer",
"@react-navigation/bottom-tabs",
"@react-navigation/material-top-tabs",
"@react-navigation/material-bottom-tabs"
"@react-navigation/material-bottom-tabs",
"@react-navigation/devtools"
]
},
"env": { "browser": true, "node": true }
Expand Down
21 changes: 11 additions & 10 deletions README.md
Expand Up @@ -12,16 +12,17 @@ If you are looking for version 4, the code can be found in the [4.x branch](http

## Package Versions

| Name | Latest Version |
| ------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| [@react-navigation/core](/packages/core) | [![badge](https://img.shields.io/npm/v/@react-navigation/core.svg?style=flat-square)](https://www.npmjs.com/package/@react-navigation/core) |
| [@react-navigation/native](/packages/native) | [![badge](https://img.shields.io/npm/v/@react-navigation/native.svg?style=flat-square)](https://www.npmjs.com/package/@react-navigation/native) |
| [@react-navigation/routers](/packages/routers) | [![badge](https://img.shields.io/npm/v/@react-navigation/routers.svg?style=flat-square)](https://www.npmjs.com/package/@react-navigation/routers) |
| [@react-navigation/stack](/packages/stack) | [![badge](https://img.shields.io/npm/v/@react-navigation/stack.svg?style=flat-square)](https://www.npmjs.com/package/@react-navigation/stack) |
| [@react-navigation/drawer](/packages/drawer) | [![badge](https://img.shields.io/npm/v/@react-navigation/drawer.svg?style=flat-square)](https://www.npmjs.com/package/@react-navigation/drawer) |
| [@react-navigation/material-top-tabs](/packages/material-top-tabs) | [![badge](https://img.shields.io/npm/v/@react-navigation/material-top-tabs.svg?style=flat-square)](https://www.npmjs.com/package/@react-navigation/material-top-tabs) |
| [@react-navigation/material-bottom-tabs](/packages/material-bottom-tabs) | [![badge](https://img.shields.io/npm/v/@react-navigation/material-bottom-tabs.svg?style=flat-square)](https://www.npmjs.com/package/@react-navigation/material-bottom-tabs) |
| [@react-navigation/bottom-tabs](/packages/bottom-tabs) | [![badge](https://img.shields.io/npm/v/@react-navigation/bottom-tabs.svg?style=flat-square)](https://www.npmjs.com/package/@react-navigation/bottom-tabs) |
| Name | Latest Version |
| ------------------------------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| [@react-navigation/core](/packages/core) | [![badge](https://img.shields.io/npm/v/@react-navigation/core.svg?style=flat-square)](https://www.npmjs.com/package/@react-navigation/core) |
| [@react-navigation/native](/packages/native) | [![badge](https://img.shields.io/npm/v/@react-navigation/native.svg?style=flat-square)](https://www.npmjs.com/package/@react-navigation/native) |
| [@react-navigation/routers](/packages/routers) | [![badge](https://img.shields.io/npm/v/@react-navigation/routers.svg?style=flat-square)](https://www.npmjs.com/package/@react-navigation/routers) |
| [@react-navigation/stack](/packages/stack) | [![badge](https://img.shields.io/npm/v/@react-navigation/stack.svg?style=flat-square)](https://www.npmjs.com/package/@react-navigation/stack) |
| [@react-navigation/drawer](/packages/drawer) | [![badge](https://img.shields.io/npm/v/@react-navigation/drawer.svg?style=flat-square)](https://www.npmjs.com/package/@react-navigation/drawer) |
| [@react-navigation/material-top-tabs](/packages/material-top-tabs) | [![badge](https://img.shields.io/npm/v/@react-navigation/material-top-tabs.svg?style=flat-square)](https://www.npmjs.com/package/@react-navigation/material-top-tabs) |
| [@react-navigation/material-bottom-tabs](/packages/material-bottom-tabs) | [![badge](https://img.shields.io/npm/v/@react-navigation/material-bottom-tabs.svg?style=flat-square)](https://www.npmjs.com/package/@react-navigation/material-bottom-tabs) |
| [@react-navigation/bottom-tabs](/packages/bottom-tabs) | [![badge](https://img.shields.io/npm/v/@react-navigation/bottom-tabs.svg?style=flat-square)](https://www.npmjs.com/package/@react-navigation/bottom-tabs) |
| [@react-navigation/devtools](/packages/devtools) | [![badge](https://img.shields.io/npm/v/@react-navigation/devtools.svg?style=flat-square)](https://www.npmjs.com/package/@react-navigation/devtools) |

## Contributing

Expand Down
10 changes: 7 additions & 3 deletions example/src/index.tsx
Expand Up @@ -27,6 +27,7 @@ import {
DefaultTheme,
DarkTheme,
PathConfig,
NavigationContainerRef,
} from '@react-navigation/native';
import {
createDrawerNavigator,
Expand All @@ -37,6 +38,7 @@ import {
StackNavigationProp,
HeaderStyleInterpolators,
} from '@react-navigation/stack';
import { useReduxDevToolsExtension } from '@react-navigation/devtools';

import { restartApp } from './Restart';
import AsyncStorage from './AsyncStorage';
Expand All @@ -60,9 +62,6 @@ YellowBox.ignoreWarnings(['Require cycle:', 'Warning: Async Storage']);

enableScreens();

// @ts-ignore
global.REACT_NAVIGATION_REDUX_DEVTOOLS_EXTENSION_INTEGRATION_ENABLED = true;

type RootDrawerParamList = {
Root: undefined;
Another: undefined;
Expand Down Expand Up @@ -192,6 +191,10 @@ export default function App() {
return () => Dimensions.removeEventListener('change', onDimensionsChange);
}, []);

const navigationRef = React.useRef<NavigationContainerRef>(null);

useReduxDevToolsExtension(navigationRef);

if (!isReady) {
return null;
}
Expand All @@ -204,6 +207,7 @@ export default function App() {
<StatusBar barStyle={theme.dark ? 'light-content' : 'dark-content'} />
)}
<NavigationContainer
ref={navigationRef}
initialState={initialState}
onStateChange={(state) =>
AsyncStorage.setItem(
Expand Down
64 changes: 27 additions & 37 deletions packages/core/src/BaseNavigationContainer.tsx
Expand Up @@ -11,7 +11,6 @@ import EnsureSingleNavigator from './EnsureSingleNavigator';
import NavigationBuilderContext from './NavigationBuilderContext';
import { ScheduleUpdateContext } from './useScheduleUpdate';
import useFocusedListeners from './useFocusedListeners';
import useDevTools from './useDevTools';
import useStateGetters from './useStateGetters';
import useOptionsGetters from './useOptionsGetters';
import useEventEmitter from './useEventEmitter';
Expand All @@ -23,14 +22,26 @@ import NavigationStateContext from './NavigationStateContext';

type State = NavigationState | PartialState<NavigationState> | undefined;

const DEVTOOLS_CONFIG_KEY =
'REACT_NAVIGATION_REDUX_DEVTOOLS_EXTENSION_INTEGRATION_ENABLED';

const NOT_INITIALIZED_ERROR =
"The 'navigation' object hasn't been initialized yet. This might happen if you don't have a navigator mounted, or if the navigator hasn't finished mounting. See https://reactnavigation.org/docs/navigating-without-navigation-prop#handling-initialization for more details.";

let hasWarnedForSerialization = false;

/**
* Migration instructions for removal of devtools from core
*/
Object.defineProperty(
global,
'REACT_NAVIGATION_REDUX_DEVTOOLS_EXTENSION_INTEGRATION_ENABLED',
{
set(_) {
console.warn(
"Redux devtools extension integration can be enabled with the '@react-navigation/devtools' package. For more details, see https://reactnavigation.org/docs/devtools"
);
},
}
);

/**
* Remove `key` and `routeNames` from the state objects recursively to get partial state.
*
Expand Down Expand Up @@ -100,7 +111,6 @@ const BaseNavigationContainer = React.forwardRef(
);

const isFirstMountRef = React.useRef<boolean>(true);
const skipTrackingRef = React.useRef<boolean>(false);

const navigatorKeyRef = React.useRef<string | undefined>();

Expand All @@ -110,23 +120,6 @@ const BaseNavigationContainer = React.forwardRef(
navigatorKeyRef.current = key;
}, []);

const reset = React.useCallback(
(state: NavigationState) => {
skipTrackingRef.current = true;
setState(state);
},
[setState]
);

const { trackState, trackAction } = useDevTools({
enabled:
// @ts-ignore
DEVTOOLS_CONFIG_KEY in global ? global[DEVTOOLS_CONFIG_KEY] : false,
name: '@react-navigation',
reset,
state,
});

const {
listeners,
addListener: addFocusedListener,
Expand Down Expand Up @@ -162,10 +155,9 @@ const BaseNavigationContainer = React.forwardRef(

const resetRoot = React.useCallback(
(state?: PartialState<NavigationState> | NavigationState) => {
trackAction('@@RESET_ROOT');
setState(state);
},
[setState, trackAction]
[setState]
);

const getRootState = React.useCallback(() => {
Expand Down Expand Up @@ -211,13 +203,20 @@ const BaseNavigationContainer = React.forwardRef(
getCurrentOptions,
}));

const onDispatchAction = React.useCallback(
(action: NavigationAction, noop: boolean) => {
emitter.emit({ type: '__unsafe_action__', data: { action, noop } });
},
[emitter]
);

const builderContext = React.useMemo(
() => ({
addFocusedListener,
addStateGetter,
trackAction,
onDispatchAction,
}),
[addFocusedListener, trackAction, addStateGetter]
[addFocusedListener, addStateGetter, onDispatchAction]
);

const scheduleContext = React.useMemo(
Expand Down Expand Up @@ -258,23 +257,14 @@ const BaseNavigationContainer = React.forwardRef(
}
}

emitter.emit({
type: 'state',
data: { state },
});

if (skipTrackingRef.current) {
skipTrackingRef.current = false;
} else {
trackState(getRootState);
}
emitter.emit({ type: 'state', data: { state } });

if (!isFirstMountRef.current && onStateChangeRef.current) {
onStateChangeRef.current(getRootState());
}

isFirstMountRef.current = false;
}, [trackState, getRootState, emitter, state]);
}, [getRootState, emitter, state]);

return (
<ScheduleUpdateContext.Provider value={scheduleContext}>
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/NavigationBuilderContext.tsx
Expand Up @@ -32,10 +32,10 @@ const NavigationBuilderContext = React.createContext<{
addActionListener?: (listener: ChildActionListener) => void;
addFocusedListener?: (listener: FocusedNavigationListener) => void;
onRouteFocus?: (key: string) => void;
onDispatchAction: (action: NavigationAction, noop: boolean) => void;
addStateGetter?: (key: string, getter: NavigatorStateGetter) => void;
trackAction: (action: NavigationAction) => void;
}>({
trackAction: () => undefined,
onDispatchAction: () => undefined,
});

export default NavigationBuilderContext;
33 changes: 32 additions & 1 deletion packages/core/src/types.tsx
Expand Up @@ -410,8 +410,39 @@ export type RouteConfig<
}
);

export type NavigationContainerEventMap = {
/**
* Event which fires when the navigation state changes.
*/
state: {
data: {
/**
* The updated state object after the state change.
*/
state: NavigationState;
};
};
/**
* Event which fires when an action is dispatched.
* Only intended for debugging purposes, don't use it for app logic.
* This event will be emitted before state changes have been applied.
*/
__unsafe_action__: {
data: {
/**
* The action object which was dispatched.
*/
action: NavigationAction;
/**
* Whether the action was a no-op, i.e. resulted any state changes.
*/
noop: boolean;
};
};
};

export type NavigationContainerRef = NavigationHelpers<ParamListBase> &
EventConsumer<{ state: { data: { state: NavigationState } } }> & {
EventConsumer<NavigationContainerEventMap> & {
/**
* Reset the navigation state of the root navigator to the provided state.
*
Expand Down
12 changes: 6 additions & 6 deletions packages/core/src/useDescriptors.tsx
Expand Up @@ -80,7 +80,7 @@ export default function useDescriptors<
emitter,
}: Options<State, ScreenOptions, EventMap>) {
const [options, setOptions] = React.useState<Record<string, object>>({});
const { trackAction } = React.useContext(NavigationBuilderContext);
const { onDispatchAction } = React.useContext(NavigationBuilderContext);

const context = React.useMemo(
() => ({
Expand All @@ -90,16 +90,16 @@ export default function useDescriptors<
addFocusedListener,
addStateGetter,
onRouteFocus,
trackAction,
onDispatchAction,
}),
[
navigation,
onAction,
addActionListener,
addFocusedListener,
onRouteFocus,
addStateGetter,
trackAction,
navigation,
onAction,
onDispatchAction,
onRouteFocus,
]
);

Expand Down

0 comments on commit 0a4ba5c

Please sign in to comment.