在React中单击时,如何防止所有的accodion组件被切换?

时间:2017-05-09 16:49:18

标签: reactjs jsx

我创建了一个自定义的Accordion组件,该组件又包含两个名为AccordionTitleAccordionContent的子组件:

AccordionTitle组件有一个按钮。点击后,AccordionContent部分会将其样式从display:none切换为block,然后再次点击时返回。

AccordionTitle.js

class AccordionTitle extends Component {
  constructor() {
    super();
    this.show = false;
  }

  toggle() {
    this.show = !this.show;
    if (this.props.onToggled) this.props.onToggled(this.show);
  }

  render() {
    return (
      <div style={this.props.style}>
        <Button onClick={e => this.toggle(e)} />
        {this.props.children}
      </div>
    );
  }
}

export default AccordionTitle;

AccordionContent.js

class AccordionContent extends Component {
  render() {
    let style = this.props.style ? this.props.style : {};
    style = JSON.parse(JSON.stringify(style));
    style.display = this.props.show ? 'block' : 'none';

    return (
      <div style={style}>
        {this.props.children}
      </div>
    );
  }
}

export default AccordionContent;

另外,我使用以下父组件:

Accordion.js

class Accordion extends Component {
  render() {
    return (
      <div>
        {this.props.children}
      </div>
    );
  }
}

Accordion.Title = AccordionTitle;
Accordion.Content = AccordionContent;

export default Accordion;

现在,当我使用Accordion组件时,我可能需要连续多个手风琴,如下所示:

ProductAccordion.js

import React, { Component } from 'react';
import Accordion from '../Accordion/Accordion';

class ProductAccordion extends Component {
  constructor() {
    super();
    this.state = {
      show: false,
    };
  }

  toggled() {
    this.setState({
      show: !this.state.show,
    });
  }

  render() {
    this.productsJsx = [];
    const products = this.props.products;

    for (let i = 0; i < products.length; i += 1) {
      this.productsJsx.push(
        <Accordion.Title onToggled={e => this.toggled(e, this)}>
          {products[i].name}
          <img src="{products[i].imgsrc}" />
        </Accordion.Title>,
        <Accordion.Content show={this.state.show}>
          {products[i].name}<br />
          {products[i].grossprice} {products[i].currency}<br />
          <hr />
        </Accordion.Content>,
      );
    }

    return (
      <Accordion style={styles.container}>
        {this.productsJsx}
      </Accordion>
    );
  }
}

export default ProductAccordion;

正如您所看到的,我抓住了来自toggled的{​​{1}}事件,并通过Accordion.Title方法将其绑定到show的道具Accordion.Content

现在,只要只有一个产品,这种方法就可以正常工作,但如果有更多产品,点击该按钮将切换所有toggled()个实例。

如何更改此选项以便只切换属于包含所点击按钮的标题的内容部分?

我还感觉组件AccordionContent应该通过允许AccordionProductAccordion事件直接委托给其兄弟来处理此问题(而不是Accordion.Titletoggled。我怎样才能做到这一点?

1 个答案:

答案 0 :(得分:1)

我建议将open项的索引存储在state中,而不是boolean。然后在您的渲染中,show={this.state.show}将类似于show={this.state.show === i}

完整示例:

import React, { Component } from 'react';
import Accordion from '../Accordion/Accordion';

class ProductAccordion extends Component {
  constructor() {
    super();
    this.state = {
      show: null,
    };
  }

  toggled(event, ind) {
    const index = this.state.index;
    this.setState({ show:ind === index ? null : ind });
  }

  render() {
    this.productsJsx = [];
    const products = this.props.products;

    for (let i = 0; i < products.length; i += 1) {
      this.productsJsx.push(
        <Accordion.Title onToggled={e => this.toggled(e, i)}>
          {products[i].name}
          <img src="{products[i].imgsrc}" />
        </Accordion.Title>,
        <Accordion.Content show={this.state.show === i}>
          {products[i].name}<br />
          {products[i].grossprice} {products[i].currency}<br />
          <hr />
        </Accordion.Content>,
      );
    }

    return (
      <Accordion style={styles.container}>
        {this.productsJsx}
      </Accordion>
    );
  }
}

export default ProductAccordion;

和这个

class AccordionTitle extends Component {
  constructor() {
    super();
  }


  render() {
    return (
      <div style={this.props.style}>
        <Button onClick={this.props.onToggled} />
        {this.props.children}
      </div>
    );
  }
}

export default AccordionTitle;