【问题标题】:Highlighting one object at a time with React使用 React 一次突出显示一个对象
【发布时间】:2015-09-25 16:03:28
【问题描述】:

我对 React 和 Javascript 都很陌生,我已经开始通过构建一个简单的可折叠菜单来学习它。它按预期工作,除了我不知道如何一次只突出显示一个项目。我怀疑 onClick 方法和状态必须由比 sectionItem 更高级别的类拥有,但我真的被困在如何使这项工作上。然后,我的第一反应是每次单击某项时遍历菜单中的所有项目,并确保所有其他项目都切换为 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> 
    );
  }
});

【问题讨论】:

    标签: javascript menu onclick reactjs


    【解决方案1】:

    伟大的开始!让我们先来看看代码中的一些问题。

    关注点分离

    无需在最顶层的组件中创建整个嵌套结构。这是非常做作的:

    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)中受益。但是,就目前而言,遵循Thinking in React 中公开的技术应该可以解决您的问题。

    确实,一种好方法是将您的“什么是开放的”状态带到Accordion 组件。你只需要让你的父母知道有什么东西被点击了。我们可以通过作为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 好运!你走在正确的道路上。

    【讨论】:

    • OP 想一次只选择一项。虽然您的 codepen 解决方案允许您选择和取消选择,但它不能解决一次仅突出显示一个 SelectionItem 的问题。
    • 是的,确实如此。我故意把这部分留给 OP 自己解决。不然他学不会吧? :)
    • 哇,多么棒且有用的答案!我真的很感谢你的努力,特别是给我一些关于其余代码的指示。我会调查一下,谢谢!
    • 嘿,很高兴为您提供帮助。
    猜你喜欢
    • 1970-01-01
    • 2022-08-10
    • 1970-01-01
    • 2017-09-18
    • 2011-03-25
    • 2010-09-12
    • 1970-01-01
    • 2013-07-30
    相关资源
    最近更新 更多