使用 React 一次突出显示一个对象

Highlighting one object at a time with React

我对 React 和 Javascript 都很陌生,我已经通过构建一个简单的可折叠菜单开始学习它。它按预期工作,除了我不知道如何一次只突出显示一个项目。我怀疑 onClick 方法和状态必须由比 sectionItem 更高级别 class 拥有,但我真的很困惑如何使它起作用。然后,我的第一直觉是每次单击某项时都遍历菜单中的所有项目,并确保所有其他项目都切换为 active=false。

这是正确的思考方式吗?有人可以解释一下在这种情况下状态在 React 中是如何工作的,以及我应该如何在这里实现它?

完整代码可在此处获得:Menu on codepen.io

这是我要突出显示的项目的代码。我还不能实现一次只突出显示一项。

var SectionItem = React.createClass({
  handleClick: function(){
    if(!this.state.active) {
      this.setState({
        currentItem: this,
        active: true,
        class: "sectionitem active"});
    }
  },
  getInitialState: function(){
     return {
       active: false,
       class: "sectionitem"
     }
  },
  render: function() {
    return (
        <div className={this.state.class} onClick={this.handleClick}>{this.props.title}</div> 
    );
  }
});

伟大的开始!让我们先解决一下代码中的一些问题。

关注点分离

无需在 top-most 组件中创建整个嵌套结构。这很做作:

for (i=0; i < this.props.menuitems.length; i++) {
  if(this.props.menuitems[i].section !== lastSection) {
    var section = this.props.menuitems[i].section;
    var items = [];
    for (j=0; j < this.props.menuitems.length; j++) {
      if(this.props.menuitems[j].section == section) {
        var itemName = this.props.menuitems[j].name;
        items.push(<SectionItem title={itemName} key={itemName} />);
      };
    }
    sections.push(<Section title={section} items={items} key={section} />);
    lastSection = section;
  }
}

恰恰相反。您应该尝试让每个组件负责呈现自己的信息。如果我们首先处理您的数据,我们可以改进这一点。问题是您的部分没有嵌套。如果不是这个...

var MENU_ITEMS = [
  {section: "About", name: "Hey", key: "Hey", selected: true},
  {section: "About", name: "No", key: "No", selected: false},
  {section: "About", name: "Way", key: "Way", selected: false},
  {section: "People", name: "Cakewalk", key: "Cakewalk", selected: false},
  {section: "People", name: "George", key: "George", selected: false},
  {section: "People", name: "Adam", key: "Adam", selected: false},
  {section: "Projects", name: "Pirate raid", key: "Pirate raid", selected: false},
  {section: "Projects", name: "Goosehunt", key: "Goosehunt", selected: false},
];

我们有这个:

var sections = [
  {
    name: "About", 
    items: [
      {name: "Hey", key: "Hey", selected: true},
      {name: "No", key: "No", selected: false},
      {name: "Way", key: "Way", selected: false}  
    ]
  },{
    name: "People", 
    items: [
      {name: "Cakewalk", key: "Cakewalk", selected: false},
      {name: "George", key: "George", selected: false},
      {name: "Adam", key: "Adam", selected: false}
    ]
  },{
    name: "Projects", 
    items: [
      {name: "Pirate raid", key: "Pirate raid", selected: false},
      {name: "Goosehunt", key: "Goosehunt", selected: false}
    ]
  }
];

然后我们可以简化Accordion很多。我们简单地为每个 section:

渲染一个 Section
var Accordion = React.createClass({  
  render: function() {
    return (
      <div className="main">
        {this.props.sections.map(function(section){
          return <Section key={section.name} section={section}/>
        })}
      </div>
    );
  }
});

同样,Section 和 SectionItem 变得非常简单。

var Section = React.createClass({
  handleClick: function(){
    this.setState({
      open: !this.state.open,
      class: this.state.open ? "section" : "section open"
    });
  },
  getInitialState: function(){
     return {
       open: false,
       class: "section"
     }
  },
  render: function() {
    return (
      <div className={this.state.class}>
        <div className="sectionhead" onClick={this.handleClick}>{this.props.section.name}</div>
        <div className="articlewrap">
          <div className="article">
            {this.props.section.items.map(function(item){
              return <SectionItem key={item.name} item={item}/>
            })}
          </div>
        </div>
      </div>
    );
  }
});

var SectionItem = React.createClass({
  handleClick: function(){
    this.setState({
      currentItem: this,
      active: !this.state.active,
      class: this.state.active ? "sectionitem" : "sectionitem active"
    });
  },
  getInitialState: function(){
     return {
       active: false,
       class: "sectionitem"
     }
  },
  render: function() {
    return (
        <div className={this.state.class} onClick={this.handleClick}>{this.props.item.name}</div> 
    );
  }
});

传播状态改变

现在,回到你原来的问题。在更复杂的应用程序中,您可以从更强大的东西中受益,例如 Flux. However, for now, following the techniques exposed in Thinking in React 应该可以解决您的问题。

确实,一种好方法是将 "what is open" 的状态带到 Accordion 组件中。您只需要让您的 parent 知道有东西被点击了。我们可以通过作为 prop.

传递的回调来做到这一点

因此,Accordion 可以有一个 openSection 状态,以及一个 onChildClick 接收被点击部分的名称。它需要将 onChildClick 传递给每个 Section.

var Accordion = React.createClass({
  getInitialState: function() {
    return {
      openSection: null
    };
  },

  onChildClick: function(sectionName) {
    this.setState({
      openSection: sectionName
    });
  },

  render: function() {
    return (
      <div className="main">
        {this.props.sections.map(function(section){
          return <Section key={section.name} 
                  onChildClick={this.onChildClick}
                  open={this.state.openSection===section.name} 
                  section={section}/>
        }.bind(this))}
      </div>
    );
  }
});

Section 只需在单击时调用此函数,并传入它自己的名称。

var Section = React.createClass({
  handleClick: function(){
    this.props.onChildClick(this.props.section.name);
  },

  render: function() {
    var className = this.props.open ? "section open" : "section"
    return (
      <div className={className}>
        <div className="sectionhead" onClick={this.handleClick}>{this.props.section.name}</div>
        <div className="articlewrap">
          <div className="article">
            {this.props.section.items.map(function(item){
              return <SectionItem key={item.name} item={item}/>
            })}
          </div>
        </div>
      </div>
    );
  }
});

您可以将此解决方案外推到 SectionItem 问题。

生成的代码笔在这里:http://codepen.io/gadr90/pen/wamQXG?editors=001

祝你学习 React 顺利!你走在正确的道路上。