在react Js中集成jquery插件时,为什么要把dom元素包裹在一个空的div中?

use*_*782 4 javascript jquery ecmascript-6 reactjs

我正在浏览Integrated-with-jquery-chosen-plugin的react文档文章,react给出了Chosen jquery插件的示例并提到了以下内容:

\n
\n

注意我们如何包裹<select>一个额外的<div>. <select>这是必要的,因为 Chosen 会在我们传递给它的节点后面附加另一个 DOM 元素。然而,就 React 而言,<div>始终只有一个孩子。这就是我们如何确保 React 更新不会与 Chosen 附加的额外 DOM 节点发生冲突。重要的是,如果您在 React 流程之外修改 DOM,则必须确保 React 没有理由触及这些 DOM 节点。

\n
\n
class Chosen extends React.Component {\n  render() {\n    return (\n      <div>        \n        <select className="Chosen-select" ref={el => this.el = el}>          \n          {this.props.children}\n        </select>\n      </div>\n    );\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

我最初认为,当<App>使用新setState()调用进行反应重新渲染时,如果我们没有额外的div内容,select那么比较算法就会遇到麻烦,并且dom后面附加的元素select将被删除。但是,显然这并不是正在发生的事情。从代码来看:

\n
<App>                   |                 |<App> \n  <Component1/>         |                 |  <Component1/>   \n  <Chosen/>             | -- rerender --> |  <Chosen/>  //gets updated and removes nodes after <select>\n  <Component2/>         |                 |  <Component2/> \n</App>                  |                 |</App>    \n
Run Code Online (Sandbox Code Playgroud)\n

我尝试模仿这种情况,令我惊讶的是,即使我不包装select在 a 中div,它也不会更新,并且在select保持不变后添加额外的 dom 节点:

\n

\r\n
\r\n
class Chosen extends React.Component {\n  render() {\n    return (\n      <div>        \n        <select className="Chosen-select" ref={el => this.el = el}>          \n          {this.props.children}\n        </select>\n      </div>\n    );\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\r\n
<App>                   |                 |<App> \n  <Component1/>         |                 |  <Component1/>   \n  <Chosen/>             | -- rerender --> |  <Chosen/>  //gets updated and removes nodes after <select>\n  <Component2/>         |                 |  <Component2/> \n</App>                  |                 |</App>    \n
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

2.5 秒后,我添加了一个红色inputdom 节点select,5 秒后,我重新渲染整个应用程序。现在为什么不通过重新渲染反应比较算法删除红色inputselect如果我把它包裹在一个额外的东西里会有什么不同div?基本上我正在寻找引用的反应文档的解释。

\n

Piw*_*oli 6

这很可能不是一个完整的答案,但太长,无法作为评论发布。

在这种特定情况下,您定义一个名为 Chosen 的 React 组件:

class Chosen extends React.Component {
  render() {
    return (
      <div>        
        <select className="Chosen-select" ref={el => this.el = el}>          
          {this.props.children}
        </select>
      </div>
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

在 React 中,一个组件永远不能返回多个“顶级”元素,因为它没有任何意义。

所以一个更简单的组件是这样的:

class Chosen extends React.Component {
  render() {
    return (
      <div>        
      </div>
      <div>
      </div>
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

是“非法的”,因为组件必须始终只返回一个“顶级”元素。如果出于某种原因您希望组件返回多个元素(如上例所示),则必须将它们包装到 div 中:

class Chosen extends React.Component {
    render() {
        return (
            <div>
                <div>
                </div>
                <div>
                </div>
            <div>
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

现在您将正确地仅返回一个顶级元素,然后该元素又将这两个 div 作为子元素。

因此,在您的情况下,如果您要这样做:

class Chosen extends React.Component {
  render() {
    return (     
        <select className="Chosen-select" ref={el => this.el = el}>          
          {this.props.children}
        </select>
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

它不会工作(即使它看起来只有一个元素),因为所选的库显然是这样工作的,因此它将将该选择组件变成这样的东西,例如:

class Chosen extends React.Component {
  render() {
    return (  
        <div class = "chosen-select-label">My select</div>   
        <div class = "chosen-select">
          <select>
            <option></option>
            <option></option>
          </select>
        </div>
        <div class = "some-other-chosen-select-shenanigans">
          ...
        </div>
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

现在 React 遇到了一个问题,因为组件返回多个元素。但是,如果将 select 包装到 div 中,结果将如下所示:

class Chosen extends React.Component {
  render() {
    return (
      <div>
        <div class = "chosen-select-label">My select</div>   
        <div class = "chosen-select">
          <select>
            <option></option>
            <option></option>
          </select>
        </div>
        <div class = "some-other-chosen-select-shenanigans">
          ...
        </div>
      </div>
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

React 会再次高兴起来。

据我所知,这种 div 包装方法是一种相当古老的方法。更现代的方法是使用React Fragments

至于为什么会存在这样的规则,我一直认为它与 JavaScript 的普通函数类似,所以这样的事情没有任何意义:

function func() {
  return 1;
  return 2;
}
Run Code Online (Sandbox Code Playgroud)

如果我们真的想返回 1 和 2,我们会将它们包装成数组之类的东西:

function func() {
  return [1, 2];
}
Run Code Online (Sandbox Code Playgroud)