I am trying to install my react-native app on my phone (iPhone). It installs alright but then crashes on start with this error message -
undefined is not an object (evaluating 'navigator.userAgent.indexOf')
<unknown>
:12:71
loadModuleImplementation
require.js:213:12
<unknown>
getScrollPosition.js:12:31
If i then enable remote js debugging, it reloads and everything works fine. I disable remote debugging, the app goes back to crashing. Any idea what might be happening here?
Update - The culprit code is in fbjs package - https://github.com/facebook/fbjs/blob/master/packages/fbjs/src/core/dom/getDocumentScrollElement.js
They check if navigator
is defined or not and then try to access the second level property on it - navigator.userAgent.indexOf
.
I can either fake navigator.userAgent
somehow or send a PR to fbjs.
navigator
is a global Web API available in browsers. This is not available in React Native as it's not a web browser. All of the Web APIs are not part of the global environment. This is why navigator.userAgent.indexOf
is throwing the undefined
error.
Now the reason why your program runs fine when debugging JS remotely is because React Native will switch to using the JavaScript engine provided by Chrome (or whatever you are debugging through) instead of the core one when you remote debug. So when debugging, you will have access to the global Web API that browsers normally have.
This is a very tricky gotcha that will often trip people up as most people develop with debugging turned on all the time. Rule of thumb: React Native is not a browser. Don't use any of the globals that you would normally use when doing web development and you'll avoid most of these types of gotcha errors.
If you would like to see how these globals are polyfilled (often with empty objects) in React Native's JavaScript engine, you'll want to look at initializeCore.js. Here you can see that React Native will polyfill navigator
like this:
// Set up Geolocation
let navigator = global.navigator;
if (navigator === undefined) {
global.navigator = navigator = {};
}
// see https://github.com/facebook/react-native/issues/10881
polyfillObjectProperty(navigator, 'product', () => 'ReactNative');
polyfillObjectProperty(navigator, 'geolocation', () => require('Geolocation'));
Edit: To answer the follow-up and give a workaround.
You can shim it, but you need to do it before importing any of your other files in index.js
(the root of your application). This is how it's done in node-libs-react-native
with their globals.js file where they too set a default user agent string. Adapting that, you can achieve the same result by doing the following:
Create file to hold your shims,
globals.js
:global.navigator.userAgent = 'React Native';
Import/Require it prior to importing any of your other files in
index.js
:import { AppRegistry } from "react-native"; import globals from "./globals"; import App from "./App"; AppRegistry.registerComponent("RNSandbox", () => App);
If for some reason you want the userAgent
to be different or be dependent on whether you are debugging or not, you can add your own logic to the globals.js
.
来源:https://stackoverflow.com/questions/49798615/app-crashes-on-start-when-remote-debugging-is-disabled