构建 ReactJS 组件,其中兄弟姐妹组件需要通信

Build ReactJS component where siblings components need to commuicate

本文关键字:组件 兄弟姐妹 通信 ReactJS 构建      更新时间:2023-09-26

所以我试图找出构建特定类型的ReactJS元素的最佳方法。

假设我有一个名为ContentArea的元素。 ContentArea 可以由许多其他自定义元素组成,即 ContentAreaHeader、ContentAreaContent 和 ContentAreaAction。 ContentArea、ContentAreaHeader 和 ContentAreaContent 基本上是包装器元素,它们使用适当的类将其子元素包装在正确的 HTML 元素中。 ContentAreaAction的实现对这个问题并不重要,只是想提到它以表明有许多不同的元素。 ContentArea 应该只有 1 个标题元素,但应该能够支持多个其他项目(ContentAreaContent 和/或 ContentAreaAction(。

一个功能是能够单击标题并切换标题旁边的其他元素的显示。 来自 AngularJS 世界,我最初的是创建一个我可以重用的指令,所以我在 ReactJS 中尝试了一下,我的代码如下所示:

var MyPage  = React.createClass({
  render: function() {
    return (
      <ContentArea>
        <ContentAreaHeader>My Header</ContentAreaHeader>
        <ContentAreaContent className={cssClasses.join(' ')}>My Content</ContentAreaContent>
      </ContentArea>
    );
  }
});

现在我可以在 MyPage 组件中添加事件和折叠状态的东西,但是我每个页面元素只能有 1 个 ContentArea 元素,或者为每个 ContentArea 复制多个元素,两者都不好。 在 AngularJS 中,每个组件都可以有自己的作用域并从其父组件继承,这可以防止此问题。

目前的解决方案是我创建了以下 mixin:

var ContentAreaCollapsableMixin = {
  getInitialState: function() {
    return {
      collapsed: false
    };
  },
  toggleCollapse: function() {
    this.setState({
      collapsed: !this.state.collapsed
    });
  }
}

现在,为了能够在每个页面元素中有多个 ContentArea 元素,我根据页面的需求创建了一个自定义 ContentArea 元素:

var MyContentArea  = React.createClass({
  mixins: [
    contentArea.mixins.collapsable
  ],
  render: function() {
    var cssClasses = [];
    console.log(this.state.collapsed);
    if(this.state.collapsed) {
      cssClasses.push('u-hide');
    }
    return (
      <ContentArea>
        <span onClick={this.toggleCollapse}><ContentAreaHeader>My Header</ContentAreaHeader></span>
        <ContentAreaContent className={cssClasses.join(' ')}>My Content</ContentAreaContent>
      </ContentArea>
    );
  }
});
var MyContentArea2  = React.createClass({
  mixins: [
    contentArea.mixins.collapsable
  ],
  render: function() {
    var cssClasses = [];
    if(this.state.collapsed) {
      cssClasses.push('u-hide');
    }
    return (
      <ContentArea>
        <span onClick={this.toggleCollapse}><ContentAreaHeader>My Header</ContentAreaHeader></span>
        <ContentAreaContent className={cssClasses.join(' ')}>My Content</ContentAreaContent>
        <ContentAreaContent className={cssClasses.join(' ')}>My Content2</ContentAreaContent>
      </ContentArea>
    );
  }
});
var ContentAreaComponents = React.createClass({
  render: function() {
    return (
      <div>
        <h1 id="test" className="test">Content Area</h1>
        <MyContentArea />
        <MyContentArea2 />
      </div>
    );
  }
});
请注意,我

正在使用跨度来附加我的事件,因为据我所知,我无法将事件附加到自定义/子元素,并且标头不应始终具有此事件,因此我不想用该内容污染标头指令(也许我可能想将该事件添加到标头中的图标而不是整个标头(。

在处理作为包装器并具有类似层次结构的元素时,这是构建此类功能的正确方法吗?

最干净的方法是将组件作为 props 传递。 例如:

<ContentArea
  header={"My Header"}
  content={[
    <div>My Content</div>,
    <div>My Other Content</div>
  ]}
  />

这在 JSX 中看起来有点奇怪,所以如果你愿意,你可以不用这样做。

React.createElement(ContentArea, {
  header: "My Header",
  content: [
    <div>My Content</div>,
    <div>My Other Content</div>
  ]
})

在 ContentArea 中,您可以像渲染 props.children 一样简单地渲染这些道具,但可以更好地控制。

var ContentArea = React.createClass({
  getInitialState: function(){ return {open: true} },
  toggleOpen: function(){ this.setState({open: !this.state.open}) },
  render: function(){
    var className = this.state.open ? "" : "hidden";
    return (
      <div>
        <ContentAreaHeader onClick={this.toggleOpen}>
          {this.props.header}
        </ContentAreaHeader>
        {this.props.content.map(function(element, index){
          return (
            <ContentAreaContent className={className} key={index}>
              {element}
            </ContentAreaContent>
          );
        })}
      </div>
    );
  }
});

此示例中生成的结构为:

<ContentArea>
  <div>
    <ContentAreaHeader>My Header</ContentAreaHeader>
    <ContentAreaContent className="..." key="0">
      <div>My Content</div>
    </ContentAreaContent>
    <ContentAreaContent className="..." key="1">
      <div>My Other Content</div>
    </ContentAreaContent>
  </div>
</ContentArea>

这是不违反任何规则的方式。 使用您提到的 API 执行此操作的方法是使用 React.Children.map 并确定它是标头还是基于索引的内容(例如 0 是标头,1..infinity 是内容(,然后将其包装在div 中以应用单击处理程序和类名。