facebook / react-native

A framework for building native applications using React
https://reactnative.dev
MIT License
118.5k stars 24.27k forks source link

document.createElement is undefined when using Chrome debugger #1149

Closed ide closed 9 years ago

ide commented 9 years ago

[I fixed the issue and have a resolution; just documenting it here for future guidance.]

In my main app file I had two requires:

var NativeModules = require('NativeModules');
var React = require('react-native');

This caused a problem because requiring NativeModules would end up requiring ExecutionEnvironment down the road, whose job it is to check if the DOM is available by testing for document.createElement. In order to force this test to fail - since React Native usually doesn't have the DOM available - InitializeJavaScriptAppEngine sets document.createElement to null. Back to the problem: this means InitializeJavaScriptAppEngine must run before ExecutionEnvironment.

The solution is to always require React before any other Facebook-provided JS, and an easy way to do that is to extract Facebook modules from React via destructuring:

var React = require('react-native');
var {
  // This pattern ensures NativeModules is not required before react-native
  NativeModules,
} = React;
sophiebits commented 9 years ago

cc @amasad -- we've hit this internally a few times as well and maybe we could have a way to pull in InitializeJavaScriptAppEngine at the top with the polyfills.

vjeux commented 9 years ago

Yeah it has come up a few times on twitter as well

amasad commented 9 years ago

Yeah seems like it should be part of polyfills. @frantic any objections to making InitializeJavaScriptAppEngine a polyfill module?

vjeux commented 9 years ago

@amasad would be awesome!

oveddan commented 9 years ago

@amasad I'm still having this issue, when using react-native 0.4.4. Here is a very simplified example which reproduces the error:

'use strict';

var React = require('react-native');
var {
  NativeModules,
  AppRegistry,
  StyleSheet,
  Text,
  ListView,
  View,
} = React;

document.createElement('div');

When I run this in the chrome, debugger, I get the error: document.createElement is not a function

What am I doing wrong?

sophiebits commented 9 years ago

@oveddan document.createElement is not supported within React Native. The bug here was that we were trying to use it at all; we shouldn't attempt to.

oveddan commented 9 years ago

Thanks for the clarification @spicyj My actual use case is to use jQuery to parse/scrape some downloaded html. I'm not using it with the intention of creating any dom nodes or elements.
When I require('jquery') without the chrome debugger it works fine, but when opening the chrome debugger, I get the error: "document.createElement is undefined"

Simple example:

'use strict';

var React = require('react-native');
var {
  NativeModules,
  AppRegistry,
  StyleSheet,
  Text,
  ListView,
  View,
} = React;

var jquery = require('jquery');

Something in jQuery is doing some testing to make sure document.createElement works, and for some reason this breaks in chrome debugger but not without it.

sophiebits commented 9 years ago

Yes, unfortunately you can't use jQuery out of the box on React Native. Maybe you can use it in conjunction with jsdom, but I don't know anyone who has tried.

oveddan commented 9 years ago

@spicyj I ended up getting jquery to work within react-native for scraping/parsing with a slight modification to it. See the issue I created within jquery here. let me know your thoughts!

vjeux commented 9 years ago

@oveddan: the proper fix is to remove window.document.createElement = null; altogether. It was used to make sure that ExecutionEnvironment.canUseDOM is false, but we modified this file and it is now always false regardless of the presence of document.

olivierlesnicki commented 9 years ago

Having the same issue when using firebase. Note that it only happens when Chrome debugger is opened.

oveddan commented 9 years ago

@olivierlesnicki can you please provide a code sample?

albertwchang commented 9 years ago

Given that I am having exactly the same problem using React Native 0.5.0, and having Chrome Inspector open... reactnativeissue

'use strict';

var React = require("react-native");
var Firebase = require("firebase");

var {
    AppRegistry,
    Component,
    StyleSheet,
    Text,
    View,
} = React;

var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#000000',
  }
});

class App extends Component {
    construtor(props) {
        super(props);
    }

    componentWillMount() {
        var backend = new Firebase("https://whatsthat.firebaseio.com");

        debugger;
    }

    render() {
        return <Text>Testing...</Text>;
    }
}

AppRegistry.registerComponent('whatsthat', () => App);
olivierlesnicki commented 9 years ago

Including and initializing an instance of Firebase is enough to replicate the issue (consistently)

mikelehen commented 9 years ago

FWIW, we'll be releasing a fix for the Firebase client shortly (hopefully today) that addresses this. The client was basically assuming that if document exists, then document.createElement will exist and work, but react-native violates this under the Chrome debugger.

olivierlesnicki commented 9 years ago

@mikelehen thanks dude!

olivierlesnicki commented 9 years ago

@mikelehen when are we open-sourcing firebase js library :-p

oveddan commented 9 years ago

@mikelehen can you link to the code that fixes this in the firebase library, just for reference for a fix for jquery?

albertwchang commented 9 years ago

@mikelehen Thanks a mil for 2.2.7. And I 2nd oveddan's request. I want to use Google Maps API, and the same problem ("document.createElement is not a function") is occurring...

oveddan commented 9 years ago

@mikelehen If you want to see the temporary solution I came up with for jquery - check this out: https://github.com/jquery/jquery/issues/2349

The issue exists because these libraries believe there is a fully functioning document available, since it's attached to window.document in the chrome or safari debugger. I'm working on a possible solution that will remove document from the global window.

You can see more on that here: #1473