不变违规:_registerComponent(...):目标容器不是DOM元素

Dan*_*mov 372 javascript dom reactjs

在制作了简单的React示例页面后,我收到此错误:

未捕获错误:不变违规:_registerComponent(...):目标容器不是DOM元素.

这是我的代码:

/** @jsx React.DOM */
'use strict';

var React = require('react');

var App = React.createClass({
  render() {
    return <h1>Yo</h1>;
  }
});

React.renderComponent(<App />, document.body);
Run Code Online (Sandbox Code Playgroud)

HTML:

<html>
<head>
  <script src="/bundle.js"></script>
</head>
<body>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

这是什么意思?

Dan*_*mov 612

到脚本执行时,document元素还不可用,因为script它本身就在head.虽然这是一个有效的解决方案,以保持scripthead渲染的DOMContentLoaded情况下,它甚至不如把你script在的最底部body ,并呈现根组件到div之前它是这样的:

<html>
<head>
</head>
<body>
  <div id="root"></div>
  <script src="/bundle.js"></script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

bundle.js,致电:

React.render(<App />, document.getElementById('root'));
Run Code Online (Sandbox Code Playgroud)

你应该总是渲染到嵌套div而不是body.否则,body当React不期望它时,各种第三方代码(Google Font Loader,浏览器插件,等等)都可以修改DOM节点,并导致非常难以跟踪和调试的奇怪错误.阅读有关此问题的更多信息

放在script底部的好处是,在脚本加载之前它不会阻止渲染,以防你将React服务器渲染添加到项目中.


更新:(2015年10月7日| v0.14)

React.render已弃用,请ReactDOM.render 改用.

例:

import ReactDOM from 'react-dom';

ReactDOM.render(<App />, document.getElementById('root'));
Run Code Online (Sandbox Code Playgroud)

  • 考虑使用defer <script defer src ="https://fb.me/react-0.14.7.js"> </ script> <script defer src ="https://fb.me/react-dom-0.14 .7.js"> </ script> <! - 您的应用程序代码 - > <script defer src ="app.js"> </ script> </ body> (4认同)
  • @thetrystero不,webpack不知道应用程序包的相对位置与它运行的DOM节点有关. (4认同)
  • 我在webpack中使用import语句仍然出现此错误.我以为webpack会处理加载脚本的顺序吗? (3认同)

Kon*_*kus 53

/index.html

<!doctype html>
<html>
  <head>
    <title>My Application</title>
    <!-- load application bundle asynchronously -->
    <script async src="/app.js"></script>
    <style type="text/css">
      /* pre-rendered critical path CSS (see isomorphic-style-loader) */
    </style>
  </head>
  <body>
    <div id="app">
      <!-- pre-rendered markup of your JavaScript app (see isomorphic apps) -->
    </div>
  </body>
</html>
Run Code Online (Sandbox Code Playgroud)

/app.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';

function run() {
  ReactDOM.render(<App />, document.getElementById('app'));
}

const loadedStates = ['complete', 'loaded', 'interactive'];

if (loadedStates.includes(document.readyState) && document.body) {
  run();
} else {
  window.addEventListener('DOMContentLoaded', run, false);
}
Run Code Online (Sandbox Code Playgroud)

(IE9 +)

注意:<script async src="..."></script>在标题中确保浏览器将加载HTML内容之前开始下载JavaScript包.

来源:React Starter Kit,isomorphic-style-loader

  • 它是`ReactDOM.render`而不是`React.render`. (5认同)

nil*_*ohn 17

准备功能,可以使用这样的:

$(document).ready(function () {
  React.render(<App />, document.body);
});
Run Code Online (Sandbox Code Playgroud)

如果您不想使用jQuery,可以使用以下onload函数:

<body onload="initReact()">...</body>
Run Code Online (Sandbox Code Playgroud)

  • 我认为`DOMContentLoaded`甚至比`load`更适合处理(这是[jQuery使用](http://stackoverflow.com/a/11523460/458193)).您也可以使用`window.addEventListener`在JS中执行此操作,无需内联处理程序和全局函数. (6认同)
  • 注意,renderComponent将被折旧.请改用React.render(). (3认同)
  • 我正在使用react-rails,因此将脚本标记移动到body标签的底部不是一个选项.`$(document).ready`解决了这个问题.哦,是的,使用`render`而不是`renderComponent`. (2认同)

taq*_*les 11

只是一个疯狂的猜测,如何添加到index.html以下:

type="javascript"
Run Code Online (Sandbox Code Playgroud)

像这样:

<script type="javascript" src="public/bundle.js"> </script>
Run Code Online (Sandbox Code Playgroud)

对我来说它有效!:-)

  • 仅供参考,这给我带来了问题,因为`type ="text/javascript"`正常工作. (3认同)

小智 6

我遇到了同样的错误。原来是由一个简单的错字在更改我的代码后引起的:

document.getElementById('root')
Run Code Online (Sandbox Code Playgroud)

document.querySelector('root')
Run Code Online (Sandbox Code Playgroud)

注意缺少的“#”它应该是

document.querySelector('#root')
Run Code Online (Sandbox Code Playgroud)

只是发布以防它帮助其他人解决此错误。


Ali*_*eza 5

是的,基本上你所做的都是对的,除了你忘记了 JavaScript 在很多情况下是同步的,所以你在DOM加载之前运行代码,有几种方法可以解决这个问题:

1)检查DOM是否完全加载,然后做你想做的,你可以听DOMContentLoaded例如:

<script>
  document.addEventListener("DOMContentLoaded", function(event) {
    console.log("DOM fully loaded and parsed");
  });
</script>
Run Code Online (Sandbox Code Playgroud)

2)非常常见的方法是将脚本标签添加到您的底部document(在 body 标签之后):

<html>
  <head>
  </head>
  <body>
  </body>
  <script src="/bundle.js"></script>
</html>
Run Code Online (Sandbox Code Playgroud)

3) 使用window.onload,在整个页面加载时触发(img 等)

window.addEventListener("load", function() {
  console.log("Everything is loaded");
});
Run Code Online (Sandbox Code Playgroud)

4) Using document.onload,当DOM准备好时它会被触发:

document.addEventListener("load", function() {
  console.log("DOM is ready");
});
Run Code Online (Sandbox Code Playgroud)

还有更多选项可以检查DOM是否准备就绪,但简短的回答是在确保DOM在每种情况下都准备就绪之前不要运行任何脚本......

JavaScript 与DOM元素一起工作,如果它们不可用,将返回null,可能会破坏整个应用程序......所以在你做之前总是确保你完全准备好运行你的 JavaScript......