UNPKG

15.4 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.default = void 0;
7
8var _routers = require("@react-navigation/routers");
9
10var React = _interopRequireWildcard(require("react"));
11
12var _checkDuplicateRouteNames = _interopRequireDefault(require("./checkDuplicateRouteNames"));
13
14var _checkSerializable = _interopRequireDefault(require("./checkSerializable"));
15
16var _createNavigationContainerRef = require("./createNavigationContainerRef");
17
18var _EnsureSingleNavigator = _interopRequireDefault(require("./EnsureSingleNavigator"));
19
20var _findFocusedRoute = _interopRequireDefault(require("./findFocusedRoute"));
21
22var _NavigationBuilderContext = _interopRequireDefault(require("./NavigationBuilderContext"));
23
24var _NavigationContainerRefContext = _interopRequireDefault(require("./NavigationContainerRefContext"));
25
26var _NavigationContext = _interopRequireDefault(require("./NavigationContext"));
27
28var _NavigationRouteContext = _interopRequireDefault(require("./NavigationRouteContext"));
29
30var _NavigationStateContext = _interopRequireDefault(require("./NavigationStateContext"));
31
32var _UnhandledActionContext = _interopRequireDefault(require("./UnhandledActionContext"));
33
34var _useChildListeners = _interopRequireDefault(require("./useChildListeners"));
35
36var _useEventEmitter = _interopRequireDefault(require("./useEventEmitter"));
37
38var _useKeyedChildListeners = _interopRequireDefault(require("./useKeyedChildListeners"));
39
40var _useOptionsGetters = _interopRequireDefault(require("./useOptionsGetters"));
41
42var _useScheduleUpdate = require("./useScheduleUpdate");
43
44var _useSyncState = _interopRequireDefault(require("./useSyncState"));
45
46function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
47
48function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
49
50function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
51
52const serializableWarnings = [];
53const duplicateNameWarnings = [];
54/**
55 * Remove `key` and `routeNames` from the state objects recursively to get partial state.
56 *
57 * @param state Initial state object.
58 */
59
60const getPartialState = state => {
61 if (state === undefined) {
62 return;
63 } // eslint-disable-next-line @typescript-eslint/no-unused-vars
64
65
66 const {
67 key,
68 routeNames,
69 ...partialState
70 } = state;
71 return { ...partialState,
72 stale: true,
73 routes: state.routes.map(route => {
74 if (route.state === undefined) {
75 return route;
76 }
77
78 return { ...route,
79 state: getPartialState(route.state)
80 };
81 })
82 };
83};
84/**
85 * Container component which holds the navigation state.
86 * This should be rendered at the root wrapping the whole app.
87 *
88 * @param props.initialState Initial state object for the navigation tree.
89 * @param props.onStateChange Callback which is called with the latest navigation state when it changes.
90 * @param props.children Child elements to render the content.
91 * @param props.ref Ref object which refers to the navigation object containing helper methods.
92 */
93
94
95const BaseNavigationContainer = /*#__PURE__*/React.forwardRef(function BaseNavigationContainer(_ref, ref) {
96 let {
97 initialState,
98 onStateChange,
99 onUnhandledAction,
100 independent,
101 children
102 } = _ref;
103 const parent = React.useContext(_NavigationStateContext.default);
104
105 if (!parent.isDefault && !independent) {
106 throw new Error("Looks like you have nested a 'NavigationContainer' inside another. Normally you need only one container at the root of the app, so this was probably an error. If this was intentional, pass 'independent={true}' explicitly. Note that this will make the child navigators disconnected from the parent and you won't be able to navigate between them.");
107 }
108
109 const [state, getState, setState, scheduleUpdate, flushUpdates] = (0, _useSyncState.default)(() => getPartialState(initialState == null ? undefined : initialState));
110 const isFirstMountRef = React.useRef(true);
111 const navigatorKeyRef = React.useRef();
112 const getKey = React.useCallback(() => navigatorKeyRef.current, []);
113 const setKey = React.useCallback(key => {
114 navigatorKeyRef.current = key;
115 }, []);
116 const {
117 listeners,
118 addListener
119 } = (0, _useChildListeners.default)();
120 const {
121 keyedListeners,
122 addKeyedListener
123 } = (0, _useKeyedChildListeners.default)();
124 const dispatch = React.useCallback(action => {
125 if (listeners.focus[0] == null) {
126 console.error(_createNavigationContainerRef.NOT_INITIALIZED_ERROR);
127 } else {
128 listeners.focus[0](navigation => navigation.dispatch(action));
129 }
130 }, [listeners.focus]);
131 const canGoBack = React.useCallback(() => {
132 if (listeners.focus[0] == null) {
133 return false;
134 }
135
136 const {
137 result,
138 handled
139 } = listeners.focus[0](navigation => navigation.canGoBack());
140
141 if (handled) {
142 return result;
143 } else {
144 return false;
145 }
146 }, [listeners.focus]);
147 const resetRoot = React.useCallback(state => {
148 var _state$key, _keyedListeners$getSt, _keyedListeners$getSt2;
149
150 const target = (_state$key = state === null || state === void 0 ? void 0 : state.key) !== null && _state$key !== void 0 ? _state$key : (_keyedListeners$getSt = (_keyedListeners$getSt2 = keyedListeners.getState).root) === null || _keyedListeners$getSt === void 0 ? void 0 : _keyedListeners$getSt.call(_keyedListeners$getSt2).key;
151
152 if (target == null) {
153 console.error(_createNavigationContainerRef.NOT_INITIALIZED_ERROR);
154 } else {
155 listeners.focus[0](navigation => navigation.dispatch({ ..._routers.CommonActions.reset(state),
156 target
157 }));
158 }
159 }, [keyedListeners.getState, listeners.focus]);
160 const getRootState = React.useCallback(() => {
161 var _keyedListeners$getSt3, _keyedListeners$getSt4;
162
163 return (_keyedListeners$getSt3 = (_keyedListeners$getSt4 = keyedListeners.getState).root) === null || _keyedListeners$getSt3 === void 0 ? void 0 : _keyedListeners$getSt3.call(_keyedListeners$getSt4);
164 }, [keyedListeners.getState]);
165 const getCurrentRoute = React.useCallback(() => {
166 const state = getRootState();
167
168 if (state == null) {
169 return undefined;
170 }
171
172 const route = (0, _findFocusedRoute.default)(state);
173 return route;
174 }, [getRootState]);
175 const emitter = (0, _useEventEmitter.default)();
176 const {
177 addOptionsGetter,
178 getCurrentOptions
179 } = (0, _useOptionsGetters.default)({});
180 const navigation = React.useMemo(() => ({ ...Object.keys(_routers.CommonActions).reduce((acc, name) => {
181 acc[name] = function () {
182 return (// @ts-expect-error: this is ok
183 dispatch(_routers.CommonActions[name](...arguments))
184 );
185 };
186
187 return acc;
188 }, {}),
189 ...emitter.create('root'),
190 dispatch,
191 resetRoot,
192 isFocused: () => true,
193 canGoBack,
194 getParent: () => undefined,
195 getState: () => stateRef.current,
196 getRootState,
197 getCurrentRoute,
198 getCurrentOptions,
199 isReady: () => listeners.focus[0] != null
200 }), [canGoBack, dispatch, emitter, getCurrentOptions, getCurrentRoute, getRootState, listeners.focus, resetRoot]);
201 React.useImperativeHandle(ref, () => navigation, [navigation]);
202 const onDispatchAction = React.useCallback((action, noop) => {
203 emitter.emit({
204 type: '__unsafe_action__',
205 data: {
206 action,
207 noop,
208 stack: stackRef.current
209 }
210 });
211 }, [emitter]);
212 const lastEmittedOptionsRef = React.useRef();
213 const onOptionsChange = React.useCallback(options => {
214 if (lastEmittedOptionsRef.current === options) {
215 return;
216 }
217
218 lastEmittedOptionsRef.current = options;
219 emitter.emit({
220 type: 'options',
221 data: {
222 options
223 }
224 });
225 }, [emitter]);
226 const stackRef = React.useRef();
227 const builderContext = React.useMemo(() => ({
228 addListener,
229 addKeyedListener,
230 onDispatchAction,
231 onOptionsChange,
232 stackRef
233 }), [addListener, addKeyedListener, onDispatchAction, onOptionsChange]);
234 const scheduleContext = React.useMemo(() => ({
235 scheduleUpdate,
236 flushUpdates
237 }), [scheduleUpdate, flushUpdates]);
238 const isInitialRef = React.useRef(true);
239 const getIsInitial = React.useCallback(() => isInitialRef.current, []);
240 const context = React.useMemo(() => ({
241 state,
242 getState,
243 setState,
244 getKey,
245 setKey,
246 getIsInitial,
247 addOptionsGetter
248 }), [state, getState, setState, getKey, setKey, getIsInitial, addOptionsGetter]);
249 const onStateChangeRef = React.useRef(onStateChange);
250 const stateRef = React.useRef(state);
251 React.useEffect(() => {
252 isInitialRef.current = false;
253 onStateChangeRef.current = onStateChange;
254 stateRef.current = state;
255 });
256 React.useEffect(() => {
257 const hydratedState = getRootState();
258
259 if (process.env.NODE_ENV !== 'production') {
260 if (hydratedState !== undefined) {
261 const serializableResult = (0, _checkSerializable.default)(hydratedState);
262
263 if (!serializableResult.serializable) {
264 const {
265 location,
266 reason
267 } = serializableResult;
268 let path = '';
269 let pointer = hydratedState;
270 let params = false;
271
272 for (let i = 0; i < location.length; i++) {
273 const curr = location[i];
274 const prev = location[i - 1];
275 pointer = pointer[curr];
276
277 if (!params && curr === 'state') {
278 continue;
279 } else if (!params && curr === 'routes') {
280 if (path) {
281 path += ' > ';
282 }
283 } else if (!params && typeof curr === 'number' && prev === 'routes') {
284 var _pointer;
285
286 path += (_pointer = pointer) === null || _pointer === void 0 ? void 0 : _pointer.name;
287 } else if (!params) {
288 path += ` > ${curr}`;
289 params = true;
290 } else {
291 if (typeof curr === 'number' || /^[0-9]+$/.test(curr)) {
292 path += `[${curr}]`;
293 } else if (/^[a-z$_]+$/i.test(curr)) {
294 path += `.${curr}`;
295 } else {
296 path += `[${JSON.stringify(curr)}]`;
297 }
298 }
299 }
300
301 const message = `Non-serializable values were found in the navigation state. Check:\n\n${path} (${reason})\n\nThis can break usage such as persisting and restoring state. This might happen if you passed non-serializable values such as function, class instances etc. in params. If you need to use components with callbacks in your options, you can use 'navigation.setOptions' instead. See https://reactnavigation.org/docs/troubleshooting#i-get-the-warning-non-serializable-values-were-found-in-the-navigation-state for more details.`;
302
303 if (!serializableWarnings.includes(message)) {
304 serializableWarnings.push(message);
305 console.warn(message);
306 }
307 }
308
309 const duplicateRouteNamesResult = (0, _checkDuplicateRouteNames.default)(hydratedState);
310
311 if (duplicateRouteNamesResult.length) {
312 const message = `Found screens with the same name nested inside one another. Check:\n${duplicateRouteNamesResult.map(locations => `\n${locations.join(', ')}`)}\n\nThis can cause confusing behavior during navigation. Consider using unique names for each screen instead.`;
313
314 if (!duplicateNameWarnings.includes(message)) {
315 duplicateNameWarnings.push(message);
316 console.warn(message);
317 }
318 }
319 }
320 }
321
322 emitter.emit({
323 type: 'state',
324 data: {
325 state
326 }
327 });
328
329 if (!isFirstMountRef.current && onStateChangeRef.current) {
330 onStateChangeRef.current(hydratedState);
331 }
332
333 isFirstMountRef.current = false;
334 }, [getRootState, emitter, state]);
335 const defaultOnUnhandledAction = React.useCallback(action => {
336 if (process.env.NODE_ENV === 'production') {
337 return;
338 }
339
340 const payload = action.payload;
341 let message = `The action '${action.type}'${payload ? ` with payload ${JSON.stringify(action.payload)}` : ''} was not handled by any navigator.`;
342
343 switch (action.type) {
344 case 'NAVIGATE':
345 case 'PUSH':
346 case 'REPLACE':
347 case 'JUMP_TO':
348 if (payload !== null && payload !== void 0 && payload.name) {
349 message += `\n\nDo you have a screen named '${payload.name}'?\n\nIf you're trying to navigate to a screen in a nested navigator, see https://reactnavigation.org/docs/nesting-navigators#navigating-to-a-screen-in-a-nested-navigator.`;
350 } else {
351 message += `\n\nYou need to pass the name of the screen to navigate to.\n\nSee https://reactnavigation.org/docs/navigation-actions for usage.`;
352 }
353
354 break;
355
356 case 'GO_BACK':
357 case 'POP':
358 case 'POP_TO_TOP':
359 message += `\n\nIs there any screen to go back to?`;
360 break;
361
362 case 'OPEN_DRAWER':
363 case 'CLOSE_DRAWER':
364 case 'TOGGLE_DRAWER':
365 message += `\n\nIs your screen inside a Drawer navigator?`;
366 break;
367 }
368
369 message += `\n\nThis is a development-only warning and won't be shown in production.`;
370 console.error(message);
371 }, []);
372 let element = /*#__PURE__*/React.createElement(_NavigationContainerRefContext.default.Provider, {
373 value: navigation
374 }, /*#__PURE__*/React.createElement(_useScheduleUpdate.ScheduleUpdateContext.Provider, {
375 value: scheduleContext
376 }, /*#__PURE__*/React.createElement(_NavigationBuilderContext.default.Provider, {
377 value: builderContext
378 }, /*#__PURE__*/React.createElement(_NavigationStateContext.default.Provider, {
379 value: context
380 }, /*#__PURE__*/React.createElement(_UnhandledActionContext.default.Provider, {
381 value: onUnhandledAction !== null && onUnhandledAction !== void 0 ? onUnhandledAction : defaultOnUnhandledAction
382 }, /*#__PURE__*/React.createElement(_EnsureSingleNavigator.default, null, children))))));
383
384 if (independent) {
385 // We need to clear any existing contexts for nested independent container to work correctly
386 element = /*#__PURE__*/React.createElement(_NavigationRouteContext.default.Provider, {
387 value: undefined
388 }, /*#__PURE__*/React.createElement(_NavigationContext.default.Provider, {
389 value: undefined
390 }, element));
391 }
392
393 return element;
394});
395var _default = BaseNavigationContainer;
396exports.default = _default;
397//# sourceMappingURL=BaseNavigationContainer.js.map
\No newline at end of file